Ваш код в порядке!
Нет реальной проблемы с синхронизацией между SetEvent и WaitForSingleObject: ключевая проблема заключается в том, что WaitForSingleObject для события будет проверять состояние события и ждать, пока оно не будет запущено.Если событие уже запущено, оно немедленно вернется.(С технической точки зрения, он запускается по уровню, а не по фронту.) Это означает, что хорошо, если SetEvent вызывается либо до, либо во время вызова WaitForSingleObject;WaitForSingleObject будет возвращаться в любом случае;или сразу, или когда SetEvent вызывается позже.
(Кстати, я предполагаю использовать событие автоматического сброса здесь. Я не могу придумать вескую причину для использования события ручного сброса; вы простов конечном итоге придется вызывать ResetEvent сразу после возвращения WaitForSingleObject, и существует опасность, что, если вы забудете об этом, вы можете в конечном итоге ожидать события, которого вы уже ждали, но забыли очистить. Кроме того, важно выполнить сброс до проверки базовойсостояние данных, в противном случае, если SetEvent вызывается между обработкой данных и вызовом Reset (), вы теряете эту информацию. Используйте функцию автоматического сброса и избегайте всего этого.)
-
[Редактировать: я неправильно прочитал код OP, выполняя по одному «pop» при каждом пробуждении, а не только при ожидании пустого, поэтому комментарии ниже относятся к коду этого сценария.Код OP на самом деле эквивалентен второму предложенному исправлению ниже.Таким образом, приведенный ниже текст на самом деле описывает довольно распространенную ошибку кодирования, когда события используются, поскольку они были семафорами, а не фактическим кодом OP.]
Но здесь есть другая проблема [или, еслибыл только один всплеск за ожидание ...], и это то, что объекты Win32 Events имеют только два состояния: не сигнализированное и сигнальное, поэтому вы можете использовать их только для отслеживания двоичного состояния, но не для подсчета.Если вы установили событие SetEvent и событие, которое уже было сигнализировано, оно остается сигнальным, и информация об этом дополнительном вызове SetEvent теряется.
В этом случае может произойти следующее:
- Элементдобавлено, SetEvent вызвано, событие теперь сигнализируется.
- Добавлен еще один элемент, SetEvent вызывается снова, событие остается сигнальным.
- Рабочий поток вызывает WaitForSingleObject, который возвращает, очищая событие,
- обрабатывается только один элемент,
- рабочий поток вызывает WaitForsingleObject, который блокируется, поскольку событие не сигнализируется, даже если в очереди все еще есть элемент.
Есть дваспособы обойти это: классический способ Comp.Sci - использовать семафор вместо события - семафоры - это, по сути, события, которые подсчитывают все вызовы 'Set';наоборот, вы можете думать о событии как о семафоре с максимальным счетом 1, который игнорирует любые другие сигналы, кроме этого.
Альтернативный способ - продолжать использовать события, но когда рабочий поток просыпается, он можеттолько предполагайте, что в очереди может быть несколько элементов, и он должен попытаться обработать их все, прежде чем он вернется к ожиданию - обычно, помещая код, который выводит элемент в цикл, который извлекает элементы и обрабатывает ихпока его пусто.Событие теперь используется не для подсчета, а для сигнализации "очередь больше не пуста".(Обратите внимание, что когда вы делаете это, вы также можете получить случаи, когда при обработке очереди вы также обрабатываете элемент, который был только что добавлен и для которого был вызван SetEvent, так что когда рабочий поток достигает WaitForSingleObject, поток просыпается, нообнаруживает, что очередь пуста, так как элемент уже обработан, поначалу это может показаться немного удивительным, но на самом деле это нормально.)
Я считаю эти два в основном эквивалентными;У обоих есть свои плюсы и минусы, но они оба верны.(Лично я предпочитаю подход, основанный на событиях, поскольку он отделяет понятие «что-то, что нужно сделать» или «доступно больше данных» от количества этой работы или данных.)