2010-07-10 48 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