EventWaitHandle - разница между WaitAny() и WaitOne()
У меня есть 3 потока, два "рабочих" и один "менеджер". Нити "Рабочие" Ожидают EventWaitHandle
, что поток "менеджера" будет сигнализировать EventWaitHandle
, после чего они увеличивают свои счетчики.
Единственное различие между этими "рабочими" потоками заключается в том, что один использует EventWaitHandle.WaitAny()
, а другой использует EventWaitHandle.WaitOne()
.
вот код:
class Program
{
static void Main(string[] args)
{
MultiThreadedJobs multyThreadedJobs = new MultiThreadedJobs();
multyThreadedJobs.Start();
Console.ReadLine();
multyThreadedJobs.Stop();
}
}
class MultiThreadedJobs : IDisposable
{
private EventWaitHandle syncEvent;
private EventWaitHandle[] syncEventsArray;
private Thread managerThread;
private Thread firstWorkerThread;
private Thread secondWorkerThread;
private volatile bool running = false;
public MultiThreadedJobs() // Ctor
{
syncEvent = new EventWaitHandle(false, EventResetMode.AutoReset, "JobsSyncEvent");
syncEventsArray = new EventWaitHandle[1];
syncEventsArray[0] = syncEvent;
managerThread = new Thread(ManagerThreadMethod);
firstWorkerThread = new Thread(FirstWorkerThreadMethod);
secondWorkerThread = new Thread(SecondWorkerThreadMethod);
}
public void Start()
{
running = true;
managerThread.Start();
firstWorkerThread.Start();
secondWorkerThread.Start();
}
public void Stop()
{
running = false;
}
private void ManagerThreadMethod() // Manager Thread
{
while (running)
{
Thread.Sleep(1000);
syncEvent.Set();
}
}
private void FirstWorkerThreadMethod() // Worker Thread
{
int counter = 0;
while (running)
{
syncEvent.WaitOne();
counter++;
}
}
private void SecondWorkerThreadMethod() // Worker Thread
{
int counter = 0;
while (running)
{
EventWaitHandle.WaitAny(syncEventsArray);
counter++;
}
}
public void Dispose()
{
syncEvent.Close();
}
}
Проблема в том, что только второй рабочий поток с EventWaitHandle.WaitAny()
всегда захватывает Событие и голодает первый рабочий поток. вместо 50/50 для каждого из них.
Ответы
Ответ 1
Вы ищете решение очень распространенной проблемы в разработке программного обеспечения, проблема производителя-потребителя. Связанная статья Википедии содержит приличную информацию об этом, в частности, показано, как сделать это неправильно.
Вы, конечно, преследуете решение, которое ошибочно. AutoResetEvent слишком упрощен. Вы уже нашли одну проблему с этим, это не обеспечивает справедливости. Много других проблем с ним, в частности, он страдает от неприятной гонки с потоками, когда поток производителей создает задания быстрее, чем потребительские потоки.
Пример кода слишком искусственен, чтобы предложить хорошую альтернативу. Блокировка низкого уровня может быть реализована классом ReaderWriterLock/Slim. Класс, который особенно хорошо подходит для решения проблем производителей/потребителей, - это класс .NET BlockingCollection. Поддержка произвольного количества потоков производителей и потребителей и обеспечение дросселирования для обеспечения того, чтобы программа не взорвалась, когда потребители не могут идти в ногу с производителями. Вы можете переписать свой образец, используя поддельный "токен", который вы передаете от производителя к потребительским потокам. A BlockingColletion<bool>
выполняет задание.
Ответ 2
Класс WaitHandle позволяет клиентам совершать асинхронный вызов и ждать:
один веб-сервис XML (WaitHandle.WaitOne),
первая из многих веб-служб XML (WaitHandle.WaitAny) или
все многие веб-службы XML (WaitHandle.WaitAll), чтобы возвращать результаты.
Если вы хотите обработать результаты по мере их поступления, вы можете использовать метод WaitHandle.WaitAny. Этот метод укажет, что одна из операций завершена и идентифицирует завершенную операцию.
Оба метода являются допустимыми. И в зависимости от переданных параметров реализация меняется. Например, метод WaitHandle.WaitAny(WaitHandle [], Int32, Boolean) ожидает, что любой из элементов в указанном массиве получит сигнал, используя 32-разрядное целое число со знаком измерить временной интервал и указать, следует ли выйти из домена синхронизации до ожидания.
WaitHandle.WaitOne Метод (Int32, Boolean) при переопределении в производном классе блокирует текущий поток
пока текущий WaitHandle не получит сигнал, используя 32-разрядное целое число со знаком для измерения интервала времени и указания
как выйти из домена синхронизации до ожидания.