C# 从同步方法调用异步方法

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/19010337/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-10 13:53:29  来源:igfitidea点击:

Calling an async method from a synchronous method

c#.netmultithreadingasynchronous

提问by Harrison

I am attempting to run async methods from a synchronous method. But I can't awaitthe asyncmethod 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 Taskoperations in order to speed up the performance. Then I want to wait for all of them to finish.

但是,在每个方法中,我想启动多个Task操作以加快性能。然后我想等他们都说完。

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

我在这里使用 await 但收到错误

The 'await' operator can only be used within an async method. Consider marking this method with the 'async' modifier and changing its return type to 'Task'

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

However, if I use the async modifier this will be an asynchronous operation. Therefore, if my call to GetData1doesn't use the await operator won't control go to GetData2 on the first await, which is what I am trying to avoid? Is it possible to keep GetData1 as a synchronous method that calls an asynchronous method? Am I designing the Asynchronous method incorrectly? As you can see I'm quite confused.

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

This could be a duplicate of How to call asynchronous method from synchronous method in 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.

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

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

采纳答案by Tim S.

Task<TResult>.Result(or Task.Wait()when there's no result) is similar to await, but is a synchronous operation. You should change GetData1()to use this. Here's the portion to change:

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

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

回答by Adam Modlin

You can call the following:

您可以拨打以下电话:

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

回答by Stephen Cleary

First, I recommend that your "internal" tasks not use Task.Runin their implementation. You should use an asyncmethod that does the CPU-bound portion synchronously.

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

Once your MyAsyncMethodis an asyncmethod that does some CPU-bound processing, then you can wrap it in a Taskand use parallel processing as such:

一旦您MyAsyncMethodasync方法执行了一些 CPU 绑定处理,那么您可以将其包装在 a 中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 SemaphoreSlimor TPL Dataflow.

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