基本的线程池问题

时间:2010-07-10 00:04:58

标签: c# .net-2.0 threadpool

让我在免责声明的前提下,我对多线程非常陌生,可能会遗漏一些明显的东西。

我目前正在使用以下代码处理目录中的所有文件。我的问题是,是否有可能一个线程完成,递减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();

2 个答案:

答案 0 :(得分: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 ();
 }
}

...

答案 1 :(得分: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);

确保在主线程终止之前处理所有文件。

希望有所帮助。