2011-05-06 72 views
2

如何在另一個線程中運行每個循環的調用,但ExternalMethod的繼續應該等待for循環(和同步)的最後一個工作線程的結束?for循環在很多線程

ExternalMethod() 
{ 
    //some calculations 
    for (int i = 0; i < 10; i++) 
    { 
     SomeMethod(i); 
    } 
    //continuation ExternalMethod 
} 
+0

如何使用Parallel For? http://msdn.microsoft.com/en-us/library/dd460703.aspx – 2011-05-06 14:49:08

+0

好,但不是在.NET 3.5 :) – Saint 2011-05-09 07:16:24

回答

5

一種方法是使用ManualResetEvent

考慮下面的代碼(注意,這不應該被視爲一個工作示例,貼在OSX所以不必VS也不是C#編譯器將手按這種過度):

static ManualResetEvent mre = new ManualResetEvent(false); 
static int DoneCount = 0; 
static int DoneRequired = 9; 
void ExternalMethod() { 
     mre.Reset(); 
     for (int i = 0; i < 10; i++) { 
       new Thread(new ThreadStart(ThreadVoid)).Start(); 
     } 
     mre.WaitOne(); 
} 

void ThreadVoid() { 
     Interlocked.Increment(ref DoneCount); 

     if (DoneCount == DoneRequired) { 
       mre.Set(); 
     } 
} 

重要提示 - 這可能不是最好的方式,只是使用ManualResetEvent的一個例子,它會滿足您的需求。

如果您使用.NET 4.0,則可以使用Parallel.For循環 - explained here

+0

作爲一個側面說明,你可以隨時抓住[單聲道:OSX](http:// www。 mono-project.com/Mono:OSX) – 2011-05-06 14:57:15

+0

不應該使用'Interlocked.Increment'而不是'++'嗎?我雖然'++'是不安全的。 – recursive 2011-05-06 14:58:05

+0

這是非常真實的,但它是一臺工作機器,並沒有合理的理由這樣做......然而;-) – 2011-05-06 14:58:33

4
System.Threading.Tasks.Parallel.For(0, 10, (i) => SomeMethod(i)); 
+0

好,但我需要.NET 3.5 :) – Saint 2011-05-06 14:53:27

0

對於.NET 3.5,也許是這樣的:

Thread[] threads = new Thread[10]; 

for (int x = 0; x < 10; x++) 
{ 
    threads[x] = new Thread(new ParameterizedThreadStart(ThreadFun)); 
    threads[x].Start(x); 
} 

foreach (Thread thread in threads) thread.Join(); 

這似乎違反直覺使用Join()的方法,但因爲你是有效的做了WaitAll型模式,它不重要的是執行連接的順序。

+0

我想在同一時間運行每個迭代paralell。只有當前線程等待結束for循環的最後一個線程。如果我理解正確,你的示例 - 每個線程都在等待早期線程 - 浪費時間在我的情況下 – Saint 2011-05-09 07:58:50

+0

thread.Join()調用導致調用(父)線程阻塞並等待被調用(子)線程完成。沒有任何子線程依賴於任何其他線程。在線程上等待什麼順序並不重要,因爲在已完成的線程上調用Join()將立即返回,並且正在運行的線程上的調用將被阻塞,直到它們完成。這樣做的好處是你的子線程不必爲了使這種技術起作用而合作。換句話說,這種技術可以用於任何想要並行運行的線程(未修改)。 – seairth 2011-05-09 12:57:52

1

一種方法是使用CountdownEvent

ExternalMethod() 
{ 
    //some calculations 
    var finished = new CountdownEvent(1); 
    for (int i = 0; i < 10; i++) 
    { 
     int capture = i; // This is needed to capture the loop variable correctly. 
     finished.AddCount(); 
     ThreadPool.QueueUserWorkItem(
      (state) => 
      { 
      try 
      { 
       SomeMethod(capture); 
      } 
      finally 
      { 
       finished.Signal(); 
      } 
      }, null); 
    } 
    finished.Signal(); 
    finished.Wait(); 
    //continuation ExternalMethod 
} 

如果CountdownEvent不可用,那麼這是另一種方法。

ExternalMethod() 
{ 
    //some calculations 
    var finished = new ManualResetEvent(false); 
    int pending = 1; 
    for (int i = 0; i < 10; i++) 
    { 
     int capture = i; // This is needed to capture the loop variable correctly. 
     Interlocked.Increment(ref pending); 
     ThreadPool.QueueUserWorkItem(
      (state) => 
      { 
      try 
      { 
       SomeMethod(capture); 
      } 
      finally 
      { 
       if (Interlocked.Decrement(ref pending) == 0) finished.Set(); 
      } 
      }, null); 
    } 
    if (Interlocked.Decrement(ref pending) == 0) finished.Set(); 
    finished.WaitOne(); 
    //continuation ExternalMethod 
} 

注意,在這兩個例子中for環本身是治療對象的並行工作項目(這是從畢竟其他工作項目一個單獨的線程),以避免如果第一個可能出現的一個非常微妙的競爭條件工作項目在下一個工作項目排隊之前通知事件。

+0

首先是.NET 4.0。第二個不會編譯,因爲「委託'System.Threading.WaitCallback'不會在ThreadPool.QueueUserWorkItem中使用'0'參數」。 – Saint 2011-05-09 07:34:32

+0

已修復。我還注意到了在lambda表達式中捕獲循環變量的另一個問題。 – 2011-05-09 12:34:33