Почему PulseEvent () ненадежен и что делать без него
Событие автосброса имеет значение king!
PulseEvent появился только в Windows NT 4.0.Он не существовал в оригинальной Windows NT 3.1.Напротив, надежные функции, такие как CreateEvent, SetEvent и WaitForMultipleObjects, существовали с момента запуска Windows NT, поэтому рассмотрите возможность их использования.
Функция CreateEvent имеет аргумент bManualReset.Если этот параметр имеет значение TRUE, функция создает объект события с ручным сбросом, для чего требуется использовать функцию ResetEvent, чтобы установить состояние события как не сигнализированное.Это не то, что вам нужно.Если этот параметр имеет значение FALSE, функция создает объект события с автосбросом, и система автоматически сбрасывает состояние события как не сигнализированное после освобождения одного ожидающего потока.
Эти события с автосбросом очень надежныи прост в использовании.
Если вы ожидаете объект события автоматического сброса с WaitForMultipleObjects или WaitForSingleObject, он надежно сбрасывает событие при выходе из этих функций ожидания.
Поэтому создайте события следующим образом:
EventHandle := CreateEvent(nil, FALSE, FALSE, nil);
Дождаться события из одного потока и выполнить SetEvent из другого потока.Это очень просто и очень надежно.
Никогда не вызывайте ResetEvent (так как он автоматически сбрасывается) или PulseEvent (так как это ненадежно и не рекомендуется).Даже Microsoft признала, что PulseEvent не следует использовать.См. https://msdn.microsoft.com/en-us/library/windows/desktop/ms684914(v=vs.85).aspx
Эта функция ненадежна и не должна использоваться, потому что будут уведомлены только те потоки, которые находятся в состоянии ожидания в момент вызова PulseEvent.Если они находятся в каком-либо другом состоянии, они не будут уведомлены, и вы никогда не узнаете наверняка, в каком состоянии находится поток.Поток, ожидающий объекта синхронизации, может быть на мгновение удален из состояния ожидания с помощью асинхронного вызова процедуры в режиме ядра, а затем возвращен в состояние ожидания после завершения APC.Если вызов PulseEvent происходит в то время, когда поток был удален из состояния ожидания, поток не будет освобожден, поскольку PulseEvent освобождает только те потоки, которые ожидают в момент его вызова.
Подробнее об асинхронных вызовах процедур в режиме ядра можно узнать по следующим ссылкам:
Мы никогда не использовали PulseEvent в наших приложениях.Что касается событий автоматического сброса, мы используем их начиная с Windows NT 3.51, и они работают очень хорошо.
Что делать, когда несколько потоков ожидают один объект
К сожалению, ваш случайнемного сложнее.У вас есть несколько потоков, ожидающих событие, и вы должны убедиться, что все потоки действительно получили уведомление.Нет другого надежного способа, кроме как создать собственное событие для каждого потока.
Вы написали, что «единственное решение, которое я вижу, состоит в том, чтобы каждый поток регистрировал использование данного объекта события в потоке владельца этого объекта».Это правильно.
Вы также написали, что «поток владельца может определить, когда безопасно сбросить состояние сигнала объекта события» - это нецелесообразно и небезопасно.Лучше всего использовать события автоматического сброса, чтобы они автоматически сбрасывались.
Итак, вам нужно иметь столько событий, сколько и потоков.Кроме того, вам нужно будет вести список зарегистрированных тем.Таким образом, чтобы уведомить все потоки, вам придется выполнить SetEvent в цикле для всех дескрипторов событий.Это очень быстрый, надежный и дешевый способ.События намного дешевле, чем темы.Таким образом, количество потоков является проблемой, а не количество событий.Практически нет ограничений на объекты ядра - ограничение для каждого процесса для дескрипторов ядра составляет 2 ^ 24.