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