2010-07-10 71 views
2

讓我先說一下這個免責聲明,我對多線程很陌生,可能會遺漏一些明顯的東西。基本的線程池問題

我目前使用下面的代碼來處理目錄中的所有文件。我的問題是,如果一個線程能夠完成,遞減numFilesLeft,並且發現它等於0,因爲下一個項目還沒有被添加爲工作項目,而不是因爲所有文件都已被處理?如果這是可能的,那麼確定它不會發生的標準方法是什麼?

謝謝你的時間。

List<Bar> bars = new List<Bar>(); 
int numFilesLeft = 0; 
ManualResetEvent isWorkDone = new ManualResetEvent(false); 

foreach (string dirName in Directory.GetDirectories(@"c:\Temp")) 
{ 
    foreach (string file in Directory.GetFiles(dirName)) 
    { 
     string temp = file; 
     Interlocked.Increment(ref numFilesLeft); 
     ThreadPool.QueueUserWorkItem(delegate 
     { 
      try 
      { 
       List<Bar> results = Process(File.ReadAllText(temp)); 
       if (results.Count > 0) 
       { 
        lock (bars) bars.AddRange(results); 
       } 
      } 
      finally 
      { 
       if (Interlocked.Decrement(ref numFilesLeft) == 0) 
       { 
        isWorkDone.Set(); 
       } 
      } 
     }); 
    } 
} 

isWorkDone.WaitOne(); 
isWorkDone.Close(); 

回答

4

是的,這是可能的。慣用的伎倆是多加一個數,排隊的項目本身的操作:

List<Bar> bars = new List<Bar>(); 
int numFilesLeft = 0; 
ManualResetEvent isWorkDone = new ManualResetEvent(false); 

Interlocked.Increment(ref numFilesLeft); 
try 
{ 
foreach (string dirName in Directory.GetDirectories(@"c:\Temp")) 
{ 
    foreach (string file in Directory.GetFiles(dirName)) 
    { 
     string temp = file; 
     Interlocked.Increment(ref numFilesLeft); 
     ThreadPool.QueueUserWorkItem(delegate 
     { 
      try 
      { 
       ... 
      } 
      finally 
      { 
       if (Interlocked.Decrement(ref numFilesLeft) == 0) 
       { 
        isWorkDone.Set(); 
       } 
      } 
     }); 
    } 
} 
} 
finally 
{ 
if (0 == Interlocked.Decrement(ref numFilesLeft)) 
{ 
    isWorkDone.Set(); 
} 
} 

... 
+0

謝謝。這似乎是一個很好的解決方案。 – Ryan 2010-07-10 00:41:14

0

我的問題是,如果將永遠是 可能一個線程來完成, 遞減numFilesLeft,並找到它 等於0,因爲下一個項目 尚未被添加爲工作項目和 不是因爲所有文件已被處理了 ?

是的。因爲我們不知道線程何時開始處理。

2.

如果這是可能的會是什麼,以確保不會發生 它 標準的方式?

我們可以使用一個ManualResetEvent數組,然後在主線程中我們將等待所有線程完成其工作。

//假設您獲得了目錄中的文件數量。

var fileCount = 10; 

ManualResetEvent[] waitHandles = new ManualResetEvent[fileCount]; 

通過文件累積並創建每個線程,就像您所做的一樣。此外,將每個ManualResetEvent作爲線程狀態傳遞給您初始化的每個線程。

...... 
    ThreadPool.QueueWorkItem(ProcessFile, waitHandles[i]); 
    ..... 

ProcessFile()方法內部重新獲得ManualResetEvent。

void ProcessFile(object stateInfo) 
{ 
    var waitHandle = stateInfo as ManualResetEvent; 
    //Do your work here 

    //finished. Call Reset() 
    waitHandle.Reset() 
} 

在主線程中,我們等待所有。

WaitHandle.WaitAll(waitHandles); 

確保在主線程終止之前處理所有文件。

希望有所幫助。

+0

這實際上和我之前使用的類似,但是我遇到了WaitAll的限制,它一次只能等待64個句柄。 – Ryan 2010-07-10 00:29:38