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

时间:2014-06-19 12:27:34

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

我有一个应用程序可以从不同的来源获取大量数据。本地数据库,联网数据库和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的功能。

2 个答案:

答案 0 :(得分:39)

听起来你真的想要这样的东西:

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没有阻止您的异步lambdas的原因是它只是等待每个Action返回...这基本上是它点击 start Func<Task>或类似的,就像你通常不想编写async void方法一样。)

答案 1 :(得分:4)

正如您在问题中所述,您的两个方法查询数据库(一个通过sql,另一个通过azure),第三个触发对Web服务的POST请求。所有这三种方法都在进行 I / O绑定工作

当您调用Parallel.Invoke时发生的事情是,您基本上会触发三个ThreadPool线程来阻止并等待基于I / O的操作完成,这几乎是浪费资源,并且如果您曾经的话,它将会非常糟糕地扩展需要。

相反,您可以使用异步apis,它们都会暴露出来:

  1. SQL Server via Entity Framework 6或ADO.NET
  2. Azure有async api的
  3. 通过HttpClient.PostAsync
  4. 发出的网络请求

    让我们假设以下方法:

    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,它可以让您节省宝贵的资源。