Стратегия ожидания двух почти синхронных событий - PullRequest
1 голос
/ 20 июля 2011

У меня есть библиотека, которую я пишу, которая обрабатывает события нажатия клавиш с помощью Win32 API. Каждый раз, когда происходит нажатие клавиши, два отдельных события происходят почти одновременно ( это и это ). В моей библиотеке есть два отдельных потока, по одному для каждого вида событий (хук и сырье).

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

Но есть проблема (конечно). В идеальном мире я бы гарантированно получал оба события постоянно. К сожалению, по причинам, которые я не понимаю, иногда Windows решает не доставлять ОДНО из двух событий мне (часто времена, когда другое приложение временно портит ввод). И поэтому, если я наберу «Hello world», поток 1 может обрабатывать «H», в то время как поток 2, возможно, никогда не получал событие «H» и переходит к «e». Таким образом, события становятся не синхронизированы, и все разрушаются в аду.

По сути, мне бы хотелось, чтобы это было так: я хочу связать события так, чтобы это имело смысл. Если поток 1 получает событие «H», а поток 2 получает «e», он должен либо (1) попытаться дождаться правильного события «H», либо (2) истечь время (да, поток 1 может изящно завершиться неудачей если это необходимо). Поскольку я знаю, что оба события должны происходить в определенном временном окне, я полагаю, что это делает задачу программирования в реальном времени.

Я ничего не знаю о программировании в реальном времени. Есть ли уже решения / структуры данных для этого? Если так, то кто они? Если нет, то каков будет общий подход к проблеме такого типа (синхронизация двух событий, близких по времени)?

Спасибо.

1 Ответ

1 голос
/ 20 июля 2011

Я не эксперт в win32, но я не могу не чувствовать, что если вы проигрываете события, в любом случае ничто не будет абсолютно надежным. Насколько я понимаю, вы пытаетесь контролировать, куда идут события клавиатуры (то есть, в какое окно они идут), основываясь на физической исходной клавиатуре - возможно, было бы лучше вместо фильтрации событий просто вводить их в первую очередь. место.

То есть, зарегистрируйте низкоуровневый хук клавиатуры , который просто отбрасывает все введенные события. Теперь используйте необработанный обработчик событий клавиатуры, чтобы внедрить эмулированные сообщения WM_KEYUP / WM_KEYDOWN в любое окно, которое должно получить их в конце.

В противном случае нам нужно (как-то) синхронизировать потоки событий WM_INPUT и WM_KEY *. Я предполагаю, что вы уже научились обмениваться данными между вашей внедренной DLL-библиотекой подключений и слушателем WM_INPUT в вашем «главном» процессе. Мы начнем с определения мьютекса с готовым ключом. Приложение, обрабатывающее хук WM_KEY *, немедленно получит этот мьютекс перед дальнейшей обработкой, чтобы избежать гонок с другими процессами, которые также могут обрабатывать ключи.

Ваш процесс обработки WM_INPUT может начать запись в кольцевой буфер совместно используемой памяти (синхронизированный с именованным мьютексом) кодов ключей, полученных через WM_INPUT, по порядку. Каждый код ключа должен иметь метку времени; Хуки WM_KEY * будут игнорировать старые коды клавиш. Обработчики WM_KEY * сначала проверяют, соответствует ли их событие самому старому не просроченному событию в буфере; если это так, уведомление должно быть отправлено ведущему процессу, и обработчик выполнит любую обработку, какую пожелает. Нет проблем.

Если в кольцевом буфере нет данных, именованный семафор можно использовать для сна. Счетчик семафора должен быть оптимистичным счетчиком количества записей, оставшихся в кольцевом буфере; получение счетчика дает процессу право и обязанность удалить один элемент из буфера, если таковой имеется, но если буфер пуст, процесс должен отбросить кольцевой мьютекс и вернуться к ожиданию семафора. Затем процедура ловушки может поставить (короткий!) Тайм-аут на ожидание в мьютексе; если время ожидания истекло, ловушка должна считать, что была рассинхронизация, и продолжить нормальную обработку. Может быть хорошей идеей будет отключить хук на короткий промежуток времени, чтобы избежать замедления быстрого набора.

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

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

...