2016-12-06 57 views
0

我遇到了涉及报告Parallel.ForEach内部进度的同步问题。我在控制台应用程序中重新创建了该问题的简化版本。该示例实际上只使用列表中的一个项目。下面的代码:IProgress <T>和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); 
     } 
    } 
} 

enter image description here

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

enter image description here

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

+0

'Progress'将在单独的线程上运行它的代码。从“Test”到它的调用将作为事件发送。所以'Parallel.ForEach'将在'Progress'处理所有'Test'发送给它的值之前完成。即使没有'Parallel.ForEach',你也会看到类似的行为。 – juharr

+0

你不需要调用'String.Format'。 – SLaks

回答

2

的解释是相当多正是上写的the docs

提供与ProgressChanged事件注册的构造函数或事件处理程序的任何处理程序是通过实例被创建时拍摄的的SynchronizationContext实例调用。 如果在构建时没有当前的SynchronizationContext,则将在ThreadPool上调用回调。

通过使用Progress<T>.Report您可以在线程池中有效排队20个任务。不能保证它们以什么顺序执行。