从同步方法调用异步方法

Har*_*son 14 .net c# multithreading asynchronous

我试图从同步方法运行异步方法.但我不能等待异步,因为我在一个同步方法方法.我不能理解TPL,因为这是我第一次使用它.

private void GetAllData()
{
    GetData1()
    GetData2()
    GetData3()
}
Run Code Online (Sandbox Code Playgroud)

每个方法都需要先前的方法来完成,因为第一个方法的数据用于第二个方法.

但是,在每个方法中我想要启动多个Task操作以加快性能.然后我想等他们所有人完成.

GetData1看起来像这样

    internal static void GetData1 ()
    {
        const int CONCURRENCY_LEVEL = 15; 
        List<Task<Data>> dataTasks = new List<Task<Data>>();
        for (int item = 0; item < TotalItems; item++)
        {
            dataTasks.Add(MyAyncMethod(State[item]));
        }
        int taskIndex = 0;
        //Schedule tasks to concurency level (or all)
        List<Task<Data>> runningTasks = new List<Task<Data>>();
        while (taskIndex < CONCURRENCY_LEVEL && taskIndex < dataTasks.Count)
        {
            runningTasks.Add(dataTasks[taskIndex]);
            taskIndex++;
        }

        //Start tasks and wait for them to finish
        while (runningTasks.Count > 0)
        {
            Task<Data> dataTask = await Task.WhenAny(runningTasks);
            runningTasks.Remove(dataTask);
            myData = await dataTask;


            //Schedule next concurrent task
            if (taskIndex < dataTasks.Count)
            {
                runningTasks.Add(dataTasks[taskIndex]);
                taskIndex++;
            }
        }
        Task.WaitAll(dataTasks.ToArray()); //This probably isn't necessary
    }
Run Code Online (Sandbox Code Playgroud)

我在这里等待,但得到一个错误

'await'运算符只能在异步方法中使用.考虑使用'async'修饰符标记此方法并将其返回类型更改为'Task'

但是,如果我使用async修饰符,这将是一个异步操作.因此,如果我的电话,以GetData1不使用的await运营商将无法控制去GetData2第一的await,这正是我想避免?是否可以将GetData1保持为调用异步方法的同步方法?我是否错误地设计了异步方法?你可以看到我很困惑.

这可能是如何从C#中的同步方法调用异步方法的重复 但是,我不知道如何申请只要是我开始多任务的解决方案,希望WaitAny为这项任务做了一点处理,然后等待所有任务移交控制权返回给调用者之前完成.

UPDATE

以下是基于以下答案的解决方案:

    private static List<T> RetrievePageTaskScheduler<T>(
        List<T> items,
        List<WebPageState> state,
        Func<WebPageState, Task<List<T>>> func)
    {
        int taskIndex = 0;

        // Schedule tasks to concurency level (or all)
        List<Task<List<T>>> runningTasks = new List<Task<List<T>>>();
        while (taskIndex < CONCURRENCY_LEVEL_PER_PROCESSOR * Environment.ProcessorCount
            && taskIndex < state.Count)
        {
            runningTasks.Add(func(state[taskIndex]));
            taskIndex++;
        }

        // Start tasks and wait for them to finish
        while (runningTasks.Count > 0)
        {
            Task<List<T>> task = Task.WhenAny(runningTasks).Result;
            runningTasks.Remove(task);

            try
            {
                items.AddRange(task.Result);
            }
            catch (AggregateException ex)
            {
                /* Throwing this exception means that if one task fails 
                 * don't process any more of them */

                // https://stackoverflow.com/questions/8853693/pattern-for-implementing-sync-methods-in-terms-of-non-parallel-task-translating
                System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(
                    ex.Flatten().InnerExceptions.First()).Throw();
            }

            // Schedule next concurrent task
            if (taskIndex < state.Count)
            {
                runningTasks.Add(func(state[taskIndex]));
                taskIndex++;
            }
        }

        return items;
    }
Run Code Online (Sandbox Code Playgroud)

Ste*_*ary 8

首先,我建议您的"内部"任务不要Task.Run在其实现中使用.您应该使用async同步执行CPU绑定部分的方法.

一旦你MyAsyncMethodasync方法执行一些CPU绑定处理,那么你可以将它包装在一个Task并使用并行处理,如下所示:

internal static void GetData1()
{
    // Start the tasks
    var dataTasks = Enumerable.Range(0, TotalItems)
        .Select(item => Task.Run(() => MyAyncMethod(State[item]))).ToList();

    // Wait for them all to complete
    Task.WaitAll(dataTasks);
}
Run Code Online (Sandbox Code Playgroud)

原始代码中的并发限制根本不起作用,因此我将其删除以获得简单性.如果要应用限制,可以使用SemaphoreSlim或TPL数据流.


Tim*_* S. 7

Task<TResult>.Result(或者Task.Wait()当没有结果时)类似于await,但是是同步操作.你应该改变GetData1()使用它.这是要改变的部分:

Task<Data> dataTask = Task.WhenAny(runningTasks).Result;
runningTasks.Remove(dataTask);
myData = gameTask.Result;
Run Code Online (Sandbox Code Playgroud)