Правильный способ реализации ThreadPool.Метода registerwaitforsingleobject

Я пытаюсь использовать ThreadPool.RegisterWaitForSingleObject для добавления таймера в набор потоков. Я создаю 9 потоков и пытаюсь дать каждому из них равный шанс на работу, так как на данный момент кажется, что происходит небольшое голодание, если я просто добавлю их в пул потоков. Я также пытаюсь реализовать событие сброса вручную, поскольку я хочу, чтобы все 9 потоков вышли перед продолжением.

каков наилучший способ гарантировать, что каждый поток в threadpool получает равное шанс на запуск в качестве функции, которую я вызываю, имеет цикл, и кажется, что каждый поток (или тот, который запускается первым) застревает в нем, а другие не получают шанс запустить.

resetEvents = new ManualResetEvent[table_seats]; 
            //Spawn 9 threads
            for (int i = 0; i < table_seats; i++)
            {
                resetEvents[i] = new ManualResetEvent(false);
                //AutoResetEvent ev = new AutoResetEvent(false);
                RegisteredWaitHandle handle = ThreadPool.RegisterWaitForSingleObject(autoEvent, ObserveSeat, (object)i, 100, false);
            }

            //wait for threads to exit
            WaitHandle.WaitAll(resetEvents);

однако не имеет значения, использую ли я resetEvents[] или ev, похоже, не работает должным образом. Могу ли я реализовать это или я (возможно) неправильно понимаю, как они должны работать.

Спасибо, Р.

1 ответов


я бы не использовать RegisterWaitForSingleObject для этой цели. Шаблоны я собираюсь здесь описать требуют Реактивные Расширения загрузить, так как вы используете .NET v3.5.

во-первых, дождаться всех рабочих элементов из ThreadPool выполнить использовать CountdownEvent класса. Это намного более элегантно и масштабируемо, чем использование нескольких ManualResetEvent экземпляров. Плюс,WaitHandle.WaitAll метод ограничен 64 ручками.

var finished = new CountdownEvent(1);
for (int i = 0; i < table_seats; i++)
{
  finished.AddCount();
  ThreadPool.QueueUserWorkItem(ObserveSeat);
    (state) =>
    {
      try
      {
        ObserveSeat(state);
      }
      finally
      {
        finished.Signal();
      }
    }, i);
}
finished.Signal();
finished.Wait();

во-вторых, вы можете попробовать позвонить Thread.Sleep(0) после нескольких итераций цикла принудительно переключить контекст, чтобы текущий поток уступил другому. Если вы хотите значительно более сложную стратегию координации, используйте Barrier класса. Добавьте еще один параметр в свой ObserveSeat функция, которая принимает этот механизм синхронизации. Вы можете предоставить его, захватив его в лямбда-выражении в коде выше.

public void ObserveSeat(object state, Barrier barrier)
{
  barrier.AddParticipant();
  try
  {
    for (int i = 0; i < NUM_ITERATIONS; i++)
    {
      if (i % AMOUNT == 0)
      {
        // Let the other threads know we are done with this phase and wait
        // for them to catch up.
        barrier.SignalAndWait();
      }
      // Perform your work here.
    }
  }
  finally
  {
    barrier.RemoveParticipant();
  }
}

обратите внимание, что, хотя этот подход, безусловно, предотвратит проблему голода, он может ограничить пропускная способность потоков. Зову SignalAndWait слишком много может вызвать много ненужного переключения контекста, но вызов слишком мало может вызвать много ненужного ожидания. Вам, вероятно, придется настроить AMOUNT чтобы получить оптимальный баланс производительности и голода. Я подозреваю,что может быть простой способ динамической настройки.