从同步方法调用异步方法

本文关键字:异步方法 调用 同步方法 | 更新日期: 2023-09-27 18:32:56

我正在尝试从同步方法运行异步方法。 但是我不能等待异步方法,因为我使用的是同步方法。 我一定不理解TPL,因为这是我使用它的第一个时间。

private void GetAllData()
{
    GetData1()
    GetData2()
    GetData3()
}

每个方法都需要前一种方法才能完成,因为第一个方法中的数据用于第二个方法。

但是,在每个方法中,我想启动多个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
    }

我在这里使用等待,但收到错误

"await"运算符只能在异步方法中使用。考虑 使用"async"修饰符标记此方法并更改其返回值 键入"任务">

但是,如果我使用异步修饰符,这将是一个异步操作。因此,如果我对GetData1的调用不使用 await 运算符,则在第一个等待时无法控制转到 GetData2,这就是我试图避免的? 是否可以将 GetData1 保留为调用异步方法的同步方法? 我是否设计了异步方法不正确?如您所见,我很困惑。

这可能是如何在 C# 中从同步方法调用异步方法的副本? 但是,我不确定如何应用那里提供的解决方案,因为我正在启动多个任务,想要WaitAny,对该任务进行更多处理,然后等待所有任务完成,然后再将控制权交还给调用方。

更新

这是我根据以下答案采用的解决方案:

    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;
    }

从同步方法调用异步方法

Task<TResult>.Result(

如果没有结果时Task.Wait()(类似于await,但是一个同步操作。您应该更改GetData1()以使用它。以下是要更改的部分:

Task<Data> dataTask = Task.WhenAny(runningTasks).Result;
runningTasks.Remove(dataTask);
myData = gameTask.Result;

首先,我建议你的"内部"任务在实现中不要使用Task.Run。应使用同步执行 CPU 密集型部分的 async 方法。

一旦您的MyAsyncMethod是执行一些 CPU 密集型处理的async方法,您就可以将其包装在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);
}

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

您可以调用以下内容:

GetData1().Wait();
GetData2().Wait();
GetData3().Wait();