简体   繁体   English

从同步方法调用异步方法

[英]Calling an async method from a synchronous method

I am attempting to run async methods from a synchronous method. 我试图从同步方法运行异步方法。 But I can't await the async method since I am in a synchronous method. 但我无法等待 异步方法,因为我在同步方法中。 I must not be understanding TPL as this is the fist time I'm using it. 我不能理解TPL,因为这是我第一次使用它。

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

Each method needs the previous method to finish as the data from the first is used for the second. 每个方法都需要先前的方法来完成,因为第一个方法的数据用于第二个方法。

However, inside each method I want to start multiple Task operations in order to speed up the performance. 但是,在每个方法中我想要启动多个Task操作以加快性能。 Then I want to wait for all of them to finish. 然后我想等他们所有人完成。

GetData1 looks like this 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
    }

I am using await here but get an Error 我在这里等待,但得到一个错误

The 'await' operator can only be used within an async method. 'await'运算符只能在异步方法中使用。 Consider marking this method with the 'async' modifier and changing its return type to 'Task' 考虑使用'async'修饰符标记此方法并将其返回类型更改为'Task'

However, if I use the async modifier this will be an asynchronous operation. 但是,如果我使用async修饰符,这将是一个异步操作。 Therefore, if my call to GetData1 doesn't use the await operator won't control go to GetData2 on the first await, which is what I am trying to avoid? 因此,如果我对GetData1调用没有使用await运算符将无法控制在第一次等待时转到GetData2,这是我试图避免的? Is it possible to keep GetData1 as a synchronous method that calls an asynchronous method? 是否可以将GetData1保持为调用异步方法的同步方法? Am I designing the Asynchronous method incorrectly? 我是否错误地设计了异步方法? As you can see I'm quite confused. 你可以看到我很困惑。

This could be a duplicate of How to call asynchronous method from synchronous method in C#? 这可能是如何从C#中的同步方法调用异步方法的重复 However, I'm not sure how to apply the solutions provided there as I'm starting multiple tasks, want to WaitAny , do a little more processing for that task, then wait for all tasks to finish before handing control back to the caller. 但是,我不确定如何应用那里提供的解决方案,因为我正在启动多个任务,想要WaitAny ,为该任务执行更多处理,然后在将控制权交还给调用者之前等待所有任务完成。

UPDATE UPDATE

Here is the solution I went with based on the answers below: 以下是基于以下答案的解决方案:

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

First, I recommend that your "internal" tasks not use Task.Run in their implementation. 首先,我建议您的“内部”任务不要在其实现中使用Task.Run You should use an async method that does the CPU-bound portion synchronously. 您应该使用async方法同步执行CPU绑定部分。

Once your MyAsyncMethod is an async method that does some CPU-bound processing, then you can wrap it in a Task and use parallel processing as such: 一旦你的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);
}

Your concurrency limiting in your original code won't work at all, so I removed it for simpilicity. 原始代码中的并发限制根本不起作用,因此我将其删除以获得简单性。 If you want to apply a limit, you can either use SemaphoreSlim or TPL Dataflow. 如果要应用限制,可以使用SemaphoreSlim或TPL Dataflow。

Task<TResult>.Result (or Task.Wait() when there's no result) is similar to await , but is a synchronous operation. Task<TResult>.Result Task.Wait() (或没有结果时为Task.Wait() )类似于await ,但是是同步操作。 You should change GetData1() to use this. 您应该更改GetData1()以使用它。 Here's the portion to change: 这是要改变的部分:

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

You can call the following: 您可以拨打以下电话:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM