2010-07-10 3 views
2

Позвольте мне предисловие к этому с отказом от ответственности, что я очень новичок в многопоточности и, возможно, что-то не хватает.Basic threadpool question

В настоящее время я использую приведенный ниже код, чтобы обработать все файлы в каталоге. Мой вопрос в том, возможно ли когда-нибудь закончить поток, уменьшится 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

0

1.

Моего вопрос, если он когда-либо будет возможно нить закончить, декремент 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