Что альтернативы являются к Win32 PulseEvent () функцией?

Документация для API Win32 PulseEvent() функция (kernel32.dll) состояния, что эта функция “… ненадежна и не должна использоваться новыми приложениями. Вместо этого используйте условные переменные”. Однако условные переменные не могут использоваться через границы процесса как (именованные) события, может.

У меня есть сценарий, который является межпроцессным, перекрестное время выполнения (собственный и управляемый код), в котором у единственного производителя иногда есть что-то интересное для сообщения нулю или большему количеству потребителей. Прямо сейчас известное именованное событие используется (и устанавливается на сообщенное состояние) производителем, использующим эту функцию PulseEvent, когда это должно сделать что-то известным. Нуль или больше потребителей ожидают на том событии (WaitForSingleObject()) и выполните действие в ответ. Нет никакой потребности в двухсторонней связи в моем сценарии, и производитель не должен знать, имеет ли событие каких-либо слушателей, и при этом это не должно знать, реагировали ли на событие успешно. С другой стороны, я не хочу, чтобы любые потребители когда-либо пропустили любые события. Другими словами, система должна быть совершенно надежной – но производитель не должен знать, если это так, или нет. Сценарий может считаться “тикером часов” – т.е. производитель обеспечивает полурегулярный сигнал для нуля или большего количества потребителей для подсчета. И у всех потребителей должно быть корректное количество за любой установленный срок времени. Никакой опрос потребителей не позволяется (причины производительности). Тикер является всего несколькими миллисекундами (приблизительно 20, но не совершенно регулярный).

У Raymen Chen (Старая Новая Вещь) есть сообщение в блоге, указывающее на “существенно дефектную” природу PulseEvent () функция, но я не вижу альтернативу для своего сценария от Chen или добавленных комментариев.

Кто-либо может предложить тот?

Следует иметь в виду, что сигнал IPC должен пересечь границы процесса на машине, не просто распараллеливает. И решение должно иметь высокую производительность, в которой потребители должны смочь действовать в 10 мс каждого события.

8
задан Bill 27 April 2010 в 23:05
поделиться

4 ответа

Думаю, вам понадобится что-то посложнее, чтобы достичь цели по надежности.

Насколько я понимаю, ваша проблема состоит в том, что у вас есть один производитель и неизвестное количество потребителей, все из которых являются разными процессами. Каждый потребитель НИКОГДА не может пропустить какие-либо мероприятия.

Я хотел бы больше пояснить, что означает пропущенное событие.

i) если потребитель начал работать и успел до того, как он дождался вашего метода уведомления и произошло событие, должен ли он обработать его, даже если он не был полностью готов к моменту отправки уведомления? (т.е.когда потребитель считается активным? когда он запускается или когда он обрабатывает свое первое событие)

ii) аналогично, если потребитель обрабатывает событие и код, ожидающий следующего уведомления, еще не начал свое ожидание (я предполагаю Подождите -> Процесс -> Цикл для ожидания структура кода), тогда должен ли он знать, что произошло другое событие, пока он зацикливался?

Я бы предположил, что i) «не совсем», поскольку это гонка между процесс запущен и "готов" и ii) "да"; то есть уведомления, по сути, ставятся в очередь для каждого потребителя, когда он присутствует, и каждый потребитель получает возможность потреблять все события, которые создаются, пока он активен, и не может их пропустить.

Итак, то, что вам нужно, - это возможность отправлять поток уведомлений набору потребителей, при этом потребитель гарантированно будет действовать в отношении всех уведомлений в этом потоке от точки, где он действует в первую очередь, до точки, в которой он отключается. т.е. если производитель создает следующий поток уведомлений

1 2 3 4 5 6 7 8 9 0

и потребитель a) запускается и обрабатывает 3, он также должен обрабатывать 4-0

, если потребитель b) запускается и обрабатывает 5, но закрывается после 9, тогда он должен был обработать 5,6,7,8,9

, если потребитель c) был запущен в момент начала уведомлений, он должен был обработать 1-0

и т. д.

Просто пульсировать событие не получится. Если потребитель не ожидает активного события, когда событие является импульсным, он пропустит событие, поэтому мы потерпим неудачу, если события будут генерироваться быстрее, чем мы можем зацикливаться, чтобы снова дождаться события.

Использование семафора также не будет работать так, как если бы один потребитель работал быстрее, чем другой, до такой степени, что он мог бы перейти к вызову семафора до того, как другой завершит обработку, и если в течение этого времени будет другое уведомление, то один потребитель может обработать событие более одного раза, и одно можно было пропустить. То есть вы можете выпустить 3 потока (если производитель знает, что есть 3 потребителя), но вы не можете гарантировать, что каждый потребитель будет выпущен только один раз.

Кольцевой буфер событий (счетчиков тиков) в разделяемой памяти, при котором каждый потребитель знает значение события, которое он обработал в последний раз, и с потребителями, предупрежденными с помощью импульсного события, должен работать за счет рассинхронизации некоторых потребителей с иногда клещи; то есть, если они пропустят один, они наверстают упущенное в следующий раз, когда у них будет пульс. Пока кольцевой буфер достаточно велик, чтобы все потребители могли обрабатывать события до того, как производитель зациклится в буфере, все должно быть в порядке.

В приведенном выше примере, если потребитель d пропускает импульс для события 4, потому что он не ждал своего события в то время, а затем переходит в режим ожидания, он будет разбужен, когда будет создано событие 5 и поскольку оно было обработано в последний раз. подсчитано 3, он обработает 4 и 5, а затем вернется к событию ...

Если этого недостаточно, я бы предложил что-то вроде PGM через сокеты, чтобы дать вам надежный многоадресная передача; преимущество этого было бы в том, что вы могли бы перемещать своих потребителей на другие машины ...

6
ответ дан 5 December 2019 в 17:35
поделиться

Если я правильно понял ваш вопрос, похоже, вы можете просто использовать SetEvent . Он освободит один поток. Просто убедитесь, что это событие с автоматическим сбросом.

Если вам нужно разрешить несколько потоков, вы можете использовать именованный семафор с CreateSemaphore . Каждый вызов ReleaseSemaphore увеличивает счетчик. Например, если счетчик равен 3 и его ожидают 3 потока, все они будут выполняться.

0
ответ дан 5 December 2019 в 17:35
поделиться

Есть две неотъемлемые проблемы с PulseEvent :

  • , если он используется с событиями автоматического сброса, он освобождает только одного официанта.
  • потоки могут никогда не пробудиться, если они будут удалены из очереди ожидания из-за APC в момент PulseEvent .

Альтернативой является широковещательная рассылка оконного сообщения и предоставление любому слушателю окна верхнего уровня, предназначенного только для сообщений, которое прослушивает это конкретное сообщение.

Основным преимуществом этого подхода является то, что вам не нужно явно блокировать поток. Недостатком этого подхода является то, что ваши слушатели должны быть STA (не может иметь очередь сообщений в потоке MTA).

Самая большая проблема с этим подходом заключается в том, что обработка события слушателем будет отложена на время, необходимое очереди, чтобы добраться до этого сообщения.

Вы также можете убедиться, что используете события ручного сброса (чтобы все ожидающие потоки пробуждались) и выполняете SetEvent / ResetEvent с небольшой задержкой (скажем, 150 мс), чтобы дать больше шансов, что потоки, временно разбуженные APC, заберут ваше мероприятие.

Конечно, сработает ли какой-либо из этих альтернативных подходов для вас, зависит от того, как часто вам нужно запускать свои события и нужны ли вам слушатели для обработки каждого события или только последнего, которое они получают.

2
ответ дан 5 December 2019 в 17:35
поделиться

Причина "ненадежности" PulseEvent не столько из-за каких-либо ошибок в самой функции, а просто из-за того, что ваш потребитель не если не ждать события в точный момент , когда вызывается PulseEvent , оно его пропустит.

В вашем сценарии, я думаю, лучшее решение - держать счетчик вручную самостоятельно. Таким образом, поток-производитель ведет счет текущего «такта», а когда поток-потребитель запускается, он считывает текущее значение этого счетчика. Затем вместо использования PulseEvent увеличьте счетчик «тактов» и используйте SetEvent , чтобы разбудить все потоки, ожидающие этого тика. Когда потребительский поток просыпается, он сравнивает свое значение «такта часов» с «тактами» производителя и узнает, сколько тактов прошло. Незадолго до того, как он снова ожидает события, он может проверить, не произошел ли еще один тик.

Я не уверен, хорошо ли я описал вышеизложенное, но надеюсь, что это даст вам представление:)

2
ответ дан 5 December 2019 в 17:35
поделиться
Другие вопросы по тегам:

Похожие вопросы: