我遇到了涉及在Parallel.ForEach中报告进度的同步问题。我在Console App中重新创建了问题的简化版本。该示例实际上仅使用列表中的一个项目。这是代码:
class Program
{
static void Main(string[] args)
{
int tracker = 0;
Parallel.ForEach(Enumerable.Range(1, 1), (item) =>
{
var progress = new Progress<int>((p) =>
{
tracker = p;
Console.WriteLine(String.Format("{0}", p));
});
Test(progress);
});
Console.WriteLine("The last value is: {0}", tracker);
Console.ReadKey();
}
static void Test(IProgress<int> progress)
{
for (int i = 0; i < 20; i++)
{
progress.Report(i);
}
}
}
正如您所看到的,我期望看到的最后一行不是最后输出而且不包含20.但是如果我删除进度报告并且只是在for循环中写入输出:< / p>
class Program
{
static void Main(string[] args)
{
int tracker = 0;
Parallel.ForEach(Enumerable.Range(1, 1), (item) =>
{
tracker = Test();
});
Console.WriteLine("The last value is: {0}", tracker);
Console.ReadKey();
}
static int Test()
{
int i;
for ( i = 0; i < 20; i++)
{
Console.WriteLine(i.ToString());
}
return i;
}
}
它表现得像我期待的那样。据我所知,Parallel.ForEach为列表中的每个项目创建一个Task,IProgress捕获它创建的上下文。鉴于它是一个控制台应用程序,我认为这不重要。求救!
答案 0 :(得分:2)
解释几乎就是the docs中所写的内容:
提供给使用ProgressChanged事件注册的构造函数或事件处理程序的任何处理程序都是通过构造实例时捕获的SynchronizationContext实例调用的。 如果在构造时没有当前的SynchronizationContext,则将在ThreadPool上调用回调。
通过使用Progress<T>.Report
,您可以有效地在线程池上排队20个任务。我们无法保证他们执行的订单是什么。