Основной вопрос пула потоков - PullRequest
2 голосов
/ 10 июля 2010

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

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

4 голосов
/ 10 июля 2010

Да, это возможно. Обычный трюк состоит в том, чтобы добавить еще один отсчет для операции постановки в очередь самих предметов:

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 голосов
/ 10 июля 2010

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);

Это гарантирует, что все файлы будут обработаны до завершения основного потока.

Надеюсь, что поможет.

...