Правильный способ реализации 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
чтобы получить оптимальный баланс производительности и голода. Я подозреваю,что может быть простой способ динамической настройки.