проблема многопоточности с WaitAll и выполненными событиями - исключение срабатывания сигнала - PullRequest
4 голосов
/ 29 июня 2011

Я использую пул потоков (что неудивительно) для управления набором потоков.То, что я пытаюсь сделать, это заставить их сигнализировать, когда это будет сделано, у меня есть:

ManualResetEvent[] doneEvents = new ManualResetEvent[char_set.Length];

public struct string_char
    {
        public string[] _str_char;
        public ManualResetEvent _doneEvent;

        public string_char(string[] str_char, ManualResetEvent doneEvent)
        {
            _str_char = str_char;
            _doneEvent = doneEvent;
        }
    }

У меня есть цикл, который создает массив char, а затем я создаю экземпляр моей структуры, заполняющей массив charи событие done:

doneEvents[no_of_elements - 1] = new ManualResetEvent(false);
            string_char s_c = new string_char(array_holder, doneEvents[no_of_elements - 1]);
            ThreadPool.QueueUserWorkItem(ThreadPoolCallback, s_c);

Итак, поток создается, добавляется в пул, и он весело выключается и запускается, после завершения он устанавливает событие done:

public void ThreadPoolCallback(Object s_c)
{
string_char _s_c = (string_char)s_c;
//do the calculations...
//when done:
_s_c._doneEvent.Set();
}

Возвращаясь к главному циклу, код ждет здесь:

WaitHandle.WaitAll(doneEvents);
Console.WriteLine("All calculations are complete.");

Проблема в том, что я получаю исключение:

'WaitAll for multiple handles on a STA thread is not supported.'

Я посмотрел это в Google, но это не такне очень помогает, что я делаю не так.Это в основном повторение примера ms msdn, кроме того, что я использую структуру, а не класс?

Я исправил проблему, используя приведенный ниже совет, переключившись на MTA (doh!) В основном;Также было полезно узнать, что максимальное число потоков составляет 64.Поэтому мне придется переключиться на другую модель ожидания, так как в конечном приложении будет запущено несколько больше!Много учиться.

Спасибо.

Ответы [ 4 ]

3 голосов
/ 29 июня 2011

Есть несколько проблем с использованием WaitHandle.WaitAll таким образом.Вы уже обнаружили один из них.Другая причина в том, что он не очень масштабируемый, поскольку имеет ограничение в 64 дескриптора.Вот шаблон, который я использую для ожидания завершения нескольких рабочих элементов.Используется класс CountdownEvent.

using (var finished = new CountdownEvent(1))
{
  foreach (var workItem in workItemCollection)
  {  
     var captured = item;
     finished.AddCount(); 
     ThreadPool.QueueUserWorkItem(
       (state) =>
       {
         try
         {
           ProcessWorkItem(captured);
         }
         finally
         {
           finished.Signal();
         }
       }, null);
  } 
  finished.Signal();
  finished.Wait();
}
2 голосов
/ 29 июня 2011

потому что в вашем Main () он определен с [STAThread], а не [MTAThread]

Вы не можете использовать WaitHandle.WaitAll() в потоке STA, так как WaitAll является блокирующим вызовом, это сделано для предотвращения работы насоса сообщений. Это не разрешено в Win32 и как таковой ни в .NET. Используйте один из других примитивов синхронизации, таких как WaitOne или Thread.Join, которые выполняют ограниченную нагрузку.

вытащил отсюда

Однако лучше выбрать новый Task, чтобы делать то, что вы хотите. здесь аналогичный пример.

0 голосов
/ 29 июня 2011

Попробуйте:

    foreach(ManualResetEvent doneEvent in doneEvents)
          WaitHandle.WaitOne(doneEvent);

    Console.WriteLine("All calculations are complete.");
0 голосов
/ 29 июня 2011

Либо пометьте свой поток MTA (если это не поток пользовательского интерфейса, который должен быть STA), либо используйте другой механизм ожидания.Например, вы можете использовать один дескриптор ожидания со счетчиком задач:

int taskCount = 0;

// Launch a thread
Interlocked.Increment(ref taskCount);

// A thread terminates
if (Interlocked.Decrement(ref taskCount) == 0)
    doneEvent.Set();
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...