[英]Parallel.Invoke does not wait for async methods to complete
我有一个应用程序,可以从不同来源提取大量数据。 本地数据库,网络数据库和Web查询。 所有这些都可能需要几秒钟才能完成。 因此,首先我决定并行运行它们:
Parallel.Invoke(
() => dataX = loadX(),
() => dataY = loadY(),
() => dataZ = loadZ()
);
不出所料,这三个都是并行执行的,但是直到最后一个完成后,整个块的执行才会返回。
接下来,我决定向应用程序添加一个微调器或“忙碌指示器”。 我不想阻止UI线程,否则微调器将不会旋转。 因此,这些需要在async
模式下运行。 但是,如果我以异步模式运行所有这三个组件,那么它们的影响会“同步”发生,只是不在与UI相同的线程中。 我仍然希望它们平行运行。
spinner.IsBusy = true;
Parallel.Invoke(
async () => dataX = await Task.Run(() => { return loadX(); }),
async () => dataY = await Task.Run(() => { return loadY(); }),
async () => dataZ = await Task.Run(() => { return loadZ(); })
);
spinner.isBusy = false;
现在,Parallel.Invoke不再等待方法完成,微调器立即关闭。 更糟糕的是,dataX / Y / Z为空,并且以后会发生异常。
这里正确的方法是什么? 我应该改用BackgroundWorker吗? 我希望利用.Net 4.5功能。
听起来您确实想要这样的东西:
spinner.IsBusy = true;
try
{
Task t1 = Task.Run(() => dataX = loadX());
Task t2 = Task.Run(() => dataY = loadY());
Task t3 = Task.Run(() => dataZ = loadZ());
await Task.WhenAll(t1, t2, t3);
}
finally
{
spinner.IsBusy = false;
}
这样,您就异步地等待所有任务完成( Task.WhenAll
返回一个在其他所有任务完成时完成的任务),而不会阻塞UI线程...而Parallel.Invoke
(和Parallel.ForEach
等)是阻止调用,并且不应在UI线程中使用。
( Parallel.Invoke
不会被异步lambda阻塞的原因是它只是在等待每个Action
返回...这基本上是在等待开始时。通常,您需要分配一个异步lambda到Func<Task>
或类似的方法,就像您通常不想编写async void
方法一样。)
如您在问题中所述,您的两个方法查询数据库(一个方法通过sql,另一个方法通过azure),第三个方法触发对Web服务的POST请求。 所有这三种方法都在进行I / O绑定工作 。
调用Parallel.Invoke
时发生的事情是,您基本上触发了三个ThreadPool线程来阻塞并等待基于I / O的操作完成,这非常浪费资源,并且在需要时会严重扩展。
取而代之的是,您可以使用异步API,它们三个都公开:
HttpClient.PostAsync
Web请求 让我们假设以下方法:
LoadXAsync();
LoadYAsync();
LoadZAsync();
您可以这样称呼他们:
spinner.IsBusy = true;
try
{
Task t1 = LoadXAsync();
Task t2 = LoadYAsync();
Task t3 = LoadZAsync();
await Task.WhenAll(t1, t2, t3);
}
finally
{
spinner.IsBusy = false;
}
这将具有相同的预期结果。 它不会冻结您的UI,它将使您节省宝贵的资源。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.