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
Calling an async method from a synchronous method
提问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 Task
operations 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 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? 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.Run
in their implementation. You should use an async
method that does the CPU-bound portion synchronously.
首先,我建议您的“内部”任务不要Task.Run
在它们的实现中使用。您应该使用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
的async
方法执行了一些 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 SemaphoreSlim
or TPL Dataflow.
您原始代码中的并发限制根本不起作用,因此为了简单起见,我将其删除。如果要应用限制,可以使用SemaphoreSlim
或 TPL Dataflow。