C# IProgress<;T>;和Parallel.ForEach同步问题

C# IProgress<;T>;和Parallel.ForEach同步问题,c#,parallel-processing,parallel.foreach,iprogress,C#,Parallel Processing,Parallel.foreach,Iprogress,我遇到了一个同步问题,涉及在Parallel.ForEach中报告进度。我在一个控制台应用程序中重新创建了这个问题的简化版本。该示例实际上只使用列表中的一项。代码如下: class Program { static void Main(string[] args) { int tracker = 0; Parallel.ForEach(Enumerable.Range(1, 1), (item) => {

我遇到了一个同步问题,涉及在Parallel.ForEach中报告进度。我在一个控制台应用程序中重新创建了这个问题的简化版本。该示例实际上只使用列表中的一项。代码如下:

 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);
        }
    }
}
类程序
{
静态void Main(字符串[]参数)
{
int-tracker=0;
Parallel.ForEach(可枚举的范围(1,1),(项)=>
{
var进度=新进度((p)=>
{
跟踪器=p;
WriteLine(String.Format(“{0}”,p));
});
测试(进度);
});
WriteLine(“最后一个值是:{0}”,tracker);
Console.ReadKey();
}
静态孔隙试验(I进度)
{
对于(int i=0;i<20;i++)
{
进度报告(一);
}
}
}

正如您所看到的,我希望最后看到的行不是最后输出的,并且不包含20。但是如果我删除了进度报告,只需像这样在for循环中写入输出:

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;
    }
}
类程序
{
静态void Main(字符串[]参数)
{
int-tracker=0;
Parallel.ForEach(可枚举的范围(1,1),(项)=>
{
跟踪器=测试();
});
WriteLine(“最后一个值是:{0}”,tracker);
Console.ReadKey();
}
静态int测试()
{
int i;
对于(i=0;i<20;i++)
{
Console.WriteLine(i.ToString());
}
返回i;
}
}


它的行为和我预期的一样。据我所知,Parallel.ForEach为列表中的每个in项创建了一个任务,而IProgress捕获了创建它的上下文。考虑到它是一个控制台应用程序,我认为这无关紧要。救命啊

这一解释与上面所写的内容几乎一模一样:

提供给构造函数的任何处理程序或注册了ProgressChanged事件的事件处理程序都将通过构造实例时捕获的SynchronizationContext实例调用如果在构建时没有当前SynchronizationContext,则将在线程池上调用回调。


使用
Progress.Report
可以有效地将线程池中的20个任务排入队列。无法保证它们的执行顺序。

进程将在单独的线程上运行它的代码。从
Test
到它的调用作为事件发送。因此,
Parallel.ForEach
将在
Progress
处理
Test
发送给它的所有值之前完成。即使没有
Parallel.ForEach
,您也会看到类似的行为。您不需要调用
String.Format