2011-12-11 142 views
5

什麼是暫停和停止(在結束之前)parallel.foreach最有效的方法?Parallel.Foreach c#暫停和停止功能?

Parallel.ForEach(list, (item) => 
{ 
    doStuff(item); 
}); 
+5

暫停?你爲什麼要暫停它? –

+0

暫停只是一個額外的功能,我想添加,如果有可能的話。我正在編寫一個在cpu和互聯網連接上都很重的程序,並且可以快速填充它,所以暫停可能不會那麼糟糕。我只是要實現停止功能並將未完成的元素列表保存到另一個列表,然後在下次運行時重寫該列表。並從停止的地方繼續。有點像暫停。謝謝。 – bbrez1

+0

如果其中一個答案爲您提供瞭解決方案[您應該將該答案標記爲已接受](http://stackoverflow.com/faq#howtoask)。 –

回答

11

Damien_The_Unbeliver有一個很好的方法,但那是隻有當你想有一些外部過程停止循環。如果你想讓循環突破,就像在普通的forforeach循環中使用break一樣,你將需要使用a overload,它具有ParallelLoopState作爲循環體的一個參數。 ParallelLoopState有兩個功能與您想要執行的操作相關,即Stop()Break()

Stop()將停止處理單元在系統的儘早這意味着更多的迭代可以調用停止(後)進行,並不能保證你停在元素之前來到的元素甚至已經開始的功能處理。

功能Break()執行完全一樣,但是Stop()它也將評估您在叫Break()該項目之前來到IEnumerable的所有元素。當你不關心處理元素的順序時,這是非常有用的,但是你必須處理所有的元素直到你停止的點。

檢查從foreach返回的ParallelLoopResult以查看該foreach是否提前停止,並且如果使用了Break(),那麼它處理的最低編號項目是什麼。

Parallel.ForEach(list, (item, loopState) => 
    { 
     bool endEarly = doStuff(item); 
     if(endEarly) 
     { 
      loopState.Break(); 
     } 
    } 
    ); 
//Equivalent to the following non parallel version, except that if doStuff ends early 
// it may or may not processed some items in the list after the break. 
foreach(var item in list) 
{ 
    bool endEarly = doStuff(item); 
    if(endEarly) 
    { 
     break; 
    } 
} 

這裏是一個更實際的例子

static bool[] list = new int[]{false, false, true, false, true, false}; 

long LowestElementTrue() 
{ 
    ParallelLoopResult result = Parallel.ForEach(list, (element, loopState) => 
    { 
     if(element) 
      loopState.Break(); 
    } 
    if(result.LowestBreakIteration.IsNull) 
     return -1; 
    else 
     return result.LowestBreakIteration.Value; 
} 

不管它如何拆分了工作後總是回到2作爲答案。

假設處理器分派兩個線程來處理這個線程,第一個線程處理元素0-2,第二個線程處理元素3-5。

 
Thread 1:    Thread 2 
0, False, continue next 3, False, continue next 
1, False, continue next 4, True, Break 
2, True, Break   5, Don't process Broke 

現在的最低指數突破是從所謂的爲2等等ParallelLoopResult.LowestBreakIteration會每次都返回2,無事的線程是如何分解,因爲它總是會處理多達數2

這裏一個例子如何如何使用Stop。

static bool[] list = new int[]{false, false, true, false, true, false}; 

long FirstElementFoundTrue() 
{ 
    long currentIndex = -1; 
    ParallelLoopResult result = Parallel.ForEach(list, (element, loopState, index) => 
    { 
     if(element) 
     { 
      loopState.Stop(); 

      //index is a 64 bit number, to make it a atomic write 
      // on 32 bit machines you must either: 
      // 1. Target 64 bit only and not allow 32 bit machines. 
      // 2. Cast the number to 32 bit. 
      // 3. Use one of the Interlocked methods. 
      Interlocked.Exchange (ref currentIndex , index); 
     } 
    } 
    return currentIndex; 
} 

根據它如何分割工作,它會返回2或4作爲答案。

假設處理器分派兩個線程來處理這個線程,第一個線程處理元素0-2,第二個線程處理元素3-5。

 
Thread 1:     Thread 2 
0, False, continue next 3, False, continue next 
1, False, continue next 4, True, Stop 
2, Don't process, Stopped 5, Don't process, Stopped 

在這種情況下,它將返回4作爲答案。讓我們看看相同的過程,但如果它處理其他元素而不是0-2和3-5。

 
Thread 1:     Thread 2 
0, False, continue next  1, False, continue next 
2, True, Stop    3, False, continue next 
4, Don't process, Stopped 5, Don't process, Stopped 

這一次,它會返回2,而不是4

2

爲了能夠停止Parallel.ForEach,你可以使用一個接受ParallelOptions參數的重載之一,幷包括在這些選項CancellationToken

有關更多詳細信息,請參閱Cancellation

至於暫停,我想不出爲什麼你想這樣做,一般來說。您可能正在尋找一個Barrier(用於協調多個線程之間的工作,也就是說他們都需要在繼續B部分之前完成A部分),但我不認爲您會使用Parallel.ForEach,因爲你不知道會有多少參與者。