我遇到Parallel.Foreach
循环问题。只要我不调用一个方法来增加父GUI程序的Progressbar值,它就可以正常工作。
其中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在下面完美地解释了这个问题。 我把我想要处理的整个工作量放到后台工作中。这完美无瑕。
答案 0 :(得分:3)
最有可能的是,您从主UI线程调用Parallel.ForEach方法。比如,响应按钮点击或其他UI事件。假设是这种情况,那么Parallel.ForEach行本身就在主UI线程上运行,并将阻止该主线程等待,直到它所请求的所有并发工作完成。
Invoke()的工作方式是它向主UI线程发送消息并等待消息得到处理。该处理完成后,将返回并继续进行。在你的代码中,当一个线程调用Invoke()时,它将在那里等待Invoke完成。但是,Invoke永远不会完成,因为主线程被Parallel.ForEach阻塞,并且无法处理更新进度条的请求,直到Parallel.ForEach完成。这是一种僵局。
有很多方法可以解决这个问题。它们基本上都等于将Parallel.ForEach从主线程中移除。最简单的方法之一,如果你可以在这里(没有足够的代码示例告诉)是使用async / await来启动任务,然后调用Parallel.ForEach并等待结果。这将释放您的主线程,以便能够处理进度条更新。
一旦你开始工作,你可能还想考虑切换到在这里使用BeginInvoke而不是Invoke,因为BeginInvoke不会在完成之前等待主线程处理消息。使用起来有点棘手,因为无法保证更新顺序,甚至在ForEach完成后也可能发生更新。它不会解决您的根问题,您必须先修复它,但它会更有效,因为Invoke()版本实际上会降低您的并行循环速度。