Pau*_*aul 20 .net c# task-parallel-library async-await
我有一个应用程序从不同的来源提取相当数量的数据.本地数据库,联网数据库和Web查询.任何这些都可能需要几秒钟才能完成.所以,首先我决定并行运行这些:
Parallel.Invoke(
() => dataX = loadX(),
() => dataY = loadY(),
() => dataZ = loadZ()
);
Run Code Online (Sandbox Code Playgroud)
正如预期的那样,所有三个并行执行,但是在最后一个块完成之前,整个块上的执行都不会返回.
接下来,我决定在应用程序中添加一个微调器或"忙指示器".我不想阻止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;
Run Code Online (Sandbox Code Playgroud)
现在,Parallel.Invoke不会等待方法完成,并且微调器立即关闭.更糟糕的是,dataX/Y/Z为空,稍后会发生异常.
这里有什么正确的方法?我应该使用BackgroundWorker吗?我希望能够利用.Net 4.5的功能.
Jon*_*eet 40
听起来你真的想要这样的东西:
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;
}
Run Code Online (Sandbox Code Playgroud)
这样你就异步地等待所有任务完成(Task.WhenAll返回一个任务,当所有其他任务完成时完成),而不阻塞UI线程...而Parallel.Invoke(和Parallel.ForEach等)阻塞调用,不应该使用在UI线程中.
(Parallel.Invoke没有阻止你的异步lambdas的原因是它只是等到每个Action返回...这基本上是它在await 开始时.通常你想要分配一个异步lambda Func<Task>或类似的,就像你async void通常不想写方法一样.)
正如您在问题中所述,两种方法查询数据库(一种通过sql,另一种通过azure)查询数据库,而第三种则触发对Web服务的POST请求。所有这三种方法都在进行I / O绑定工作。
调用时发生的事情Parallel.Invoke是,您基本上触发了三个ThreadPool线程来阻塞并等待基于I / O的操作完成,这非常浪费资源,并且在需要时会严重扩展。
取而代之的是,您可以使用异步API,它们三个都公开:
HttpClient.PostAsync让我们假设以下方法:
LoadXAsync();
LoadYAsync();
LoadZAsync();
Run Code Online (Sandbox Code Playgroud)
您可以这样称呼他们:
spinner.IsBusy = true;
try
{
Task t1 = LoadXAsync();
Task t2 = LoadYAsync();
Task t3 = LoadZAsync();
await Task.WhenAll(t1, t2, t3);
}
finally
{
spinner.IsBusy = false;
}
Run Code Online (Sandbox Code Playgroud)
这将具有相同的预期结果。它不会冻结您的UI,它将使您节省宝贵的资源。
| 归档时间: |
|
| 查看次数: |
13062 次 |
| 最近记录: |