1

我遇到了一个同步问题,涉及在 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);
        }
    }
}

在此处输入图像描述

如您所见,我希望最后看到的行不是最后输出,也不包含 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;
    }
}

在此处输入图像描述

它的行为就像我预期的那样。据我所知,Parallel.ForEach 为列表中的每个项目创建一个任务,而 IProgress 捕获创建它的上下文。鉴于它是一个控制台应用程序,我认为这并不重要。请帮忙!

4

1 回答 1

2

解释几乎正是文档中所写的内容:

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

通过使用Progress<T>.Report,您可以有效地在线程池中排队 20 个任务。无法保证它们的执行顺序。

于 2016-12-06T18:46:54.967 回答