2016-12-06 45 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個任務。不能保證它們以什麼順序執行。