Parallel.Foreach 开始使用 Invoke 空闲

Parallel.Foreach starts to idle with Invoke

Parallel.Foreach 循环有问题。只要我不调用方法来增加父 GUI 程序的进度条值,它就可以正常工作。 KeinPapierVersand 是一个简单的 List<int> 对象,EinzelnachweisDruckDatum 是一个 DateTime.

Parallel.ForEach(KeinPapierVersand, partner =>
{
    generate_PCL_nachweis(partner, EinzelnachweisDruckDatum, true, false);
    generate_BGF_Report(partner, EinzelnachweisDruckDatum, false);

    //If the following line is uncommented, the loop starts to idle after about 200 
    // processed Items and will never reach the code after the loop.

    myProgressbar_einzelnachweis_druck.Parent.BeginInvoke(new MethodInvoker(delegate 
    { 
        myProgressbar_einzelnachweis_druck.Value = 
                                         myProgressbar_einzelnachweis_druck.Value + 1; 
    }));
});

这是我在 C# 中使用并行性的第一步。我不知道这里有什么问题,没有抛出异常(之前在 Try/Catch 中)。 如果不调用进度条,循环总是会毫无问题地结束。 为什么我的逻辑不起作用?我的推理错误在哪里?请帮忙。

编辑: 这里的问题是死锁情况,Aaron 在下面对此进行了完美解释。 我将我想要处理的整个工作负载放入后台工作程序中。这完美无缺。

很可能,您是从 UI 主线程调用 Parallel.ForEach 方法。比如,响应按钮点击或其他 UI 事件。假设是这种情况,那么 Parallel.ForEach 行本身在主 UI 线程上是 运行 并且会阻塞主线程等待它请求的所有并发工作完成。

Invoke() 的工作方式是将消息发送到主 UI 线程并等待消息得到处理。该处理完成后,它将 return 并继续进行。在您的代码中,当线程调用 Invoke() 时,它将在那里等待 Invoke 完成。但是,Invoke 永远不会完成,因为主线程被 Parallel.ForEach 阻塞并且在 Parallel.ForEach 完成之前无法处理更新进度条的请求。这是一种死锁。

有很多方法可以解决这个问题。它们本质上都相当于让 Parallel.ForEach 脱离主线程。最简单的方法之一是使用 async/await 启动任务,然后调用 Parallel.ForEach 和等待结果。这将释放您的主线程,以便能够处理进度条更新。

一旦你开始工作,你可能还想考虑在这里切换到使用 BeginInvoke 而不是 Invoke,因为 BeginInvoke 在完成之前不会等待主线程处理消息。使用起来有点棘手,因为不能保证更新顺序并且更新甚至可能在 ForEach 完成后发生。它不会解决你的根本问题,你必须先解决它,但它会更有效率,因为 Invoke() 版本实际上会减慢你的并行循环。