c# - Parallel.Invoke 不等待异步方法完成

标签 c# .net task-parallel-library async-await

我有一个应用程序可以从不同来源提取大量数据。本地数据库、网络数据库和 Web 查询。这些中的任何一个都可能需要几秒钟才能完成。所以,首先我决定并行运行它们:

Parallel.Invoke(
   () => dataX = loadX(),
   () => dataY = loadY(),
   () => dataZ = loadZ()
);

正如预期的那样,所有三个并行执行,但整个 block 的执行直到最后一个完成后才会返回。

接下来,我决定向应用程序添加一个微调器或“忙碌指示器”。我不想阻塞 UI 线程,否则微调器不会旋转。所以这些需要在 async 模式下运行。但是,如果我在 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 方法一样。)

关于c# - Parallel.Invoke 不等待异步方法完成,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24306620/

相关文章:

c# - 动态链接 `Task<T>` 且不阻塞线程

c# - MVC 如何返回带有参数的 View

c# - 具有 LongRunning 状态和线程同步和性能的 TPL

c# - Task.Start 奇怪的行为

c# - 尽管有 .BMP 扩展名,但确定图像是否为 PNG

c# - 实现 INotifyPropertyChanged 时,C# 中的 [NotifyPropertyChangedInvocator] 是什么?

c# - UserPrincipal.FindByIdentity 权限

c# - ToolbarItem 图标维度

C# HttpWebRequest 获取页面总大小

c# - CancellationToken和CancellationTokenSource-如何使用?