PostMessage иногда теряет сообщение
-
22-07-2019 - |
Вопрос
Я написал многопоточное приложение для Windows, где поток:
A & # 8211; форма окна, которая обрабатывает взаимодействие с пользователем и обрабатывает данные из B.
B & # 8211; иногда генерирует данные и передает их два А.
Потокобезопасная очередь используется для передачи данных из потока B в A. Функции enqueue и dequeue охраняются с использованием объектов критического раздела Windows. Р>
Если при вызове функции enqueue очередь пуста, функция будет использовать PostMessage, чтобы сообщить A, что в очереди есть данные. Функция проверяет, чтобы убедиться, что вызов PostMessage выполнен успешно, и многократно вызывает PostMessage, если он не был успешным (PostMessage еще не удалось). Р>
Это работало довольно долго, пока один конкретный компьютер не начал терять случайное сообщение. Под потерей я подразумеваю, что PostMessage успешно возвращается в B, но A никогда не получает сообщение. Это заставляет программное обеспечение казаться замороженным. Р>
Я уже придумал пару приемлемых обходных путей. Мне интересно знать, почему Windows теряет эти сообщения и почему это происходит только на одном компьютере. Р>
Вот соответствующие части кода.
// Only called by B
procedure TSharedQueue.Enqueue(AItem: TSQItem);
var
B: boolean;
begin
EnterCriticalSection(FQueueLock);
if FCount > 0 then
begin
FLast.FNext := AItem;
FLast := AItem;
end
else
begin
FFirst := AItem;
FLast := AItem;
end;
if (FCount = 0) or (FCount mod 10 = 0) then // just in case a message is lost
repeat
B := PostMessage(FConsumer, SQ_HAS_DATA, 0, 0);
if not B then
Sleep(1000); // this line of code has never been reached
until B;
Inc(FCount);
LeaveCriticalSection(FQueueLock);
end;
// Only called by A
function TSharedQueue.Dequeue: TSQItem;
begin
EnterCriticalSection(FQueueLock);
if FCount > 0 then
begin
Result := FFirst;
FFirst := FFirst.FNext;
Result.FNext := nil;
Dec(FCount);
end
else
Result := nil;
LeaveCriticalSection(FQueueLock);
end;
// procedure called when SQ_HAS_DATA is received
procedure TfrmMonitor.SQHasData(var AMessage: TMessage);
var
Item: TSQItem;
begin
while FMessageQueue.Count > 0 do
begin
Item := FMessageQueue.Dequeue;
// use the Item somehow
end;
end;
Решение
Защищен ли FCount
также FQueueLock
? Если нет, то ваша проблема заключается в увеличении FCount
после того, как опубликованное сообщение уже обработано.
Вот что может происходить:
<Ол> PostMessage
FCount
равен 0
FCount
Быстрое решение - увеличить FCount
перед вызовом PostMessage
.
Имейте в виду, что все может произойти быстрее, чем можно было ожидать (т. е. сообщение, отправленное с PostMessage, было перехвачено и обработано другим потоком до того, как вы сможете увеличить FCount несколькими строками позже), особенно когда вы находитесь в истинная многопоточная среда (несколько процессоров). Вот почему я спросил ранее, «проблемный ли компьютер»? было несколько процессоров / ядер.
Простой способ устранения проблем, подобных этим, состоит в том, чтобы создать код с дополнительной регистрацией для регистрации при каждом вводе метода, входа / выхода из критического раздела и т. д. Затем вы можете проанализировать журнал, чтобы увидеть истинный порядок событий. р>
Отдельно отметим, что небольшая оптимизация, которую можно выполнить в сценарии «продюсер / потребитель», например, состоит в том, чтобы использовать две очереди вместо одной. Когда потребитель просыпается, чтобы обработать полную очередь, вы меняете полную очередь на пустую и просто блокируете / обрабатываете полную очередь, в то время как новая пустая очередь может быть заполнена без двух потоков, пытающихся заблокировать очереди друг друга. Тем не менее, вам все равно понадобится некоторая блокировка в обмене двумя очередями.
Другие советы
Если очередь пуста при постановке в очередь функция вызывается, функция будет используйте PostMessage, чтобы сказать A, что там это данные в очереди.
Вы блокируете очередь сообщений перед проверкой размера очереди и выдачей PostMessage
? Возможно, вы испытываете состояние гонки, когда вы проверяете очередь и обнаруживаете ее непустой, когда фактически A обрабатывает самое последнее сообщение и собирается бездействовать.
Чтобы узнать, действительно ли у вас есть состояние гонки, а не проблема с PostMessage
, вы можете переключиться на использование события. Рабочий поток (A) будет ожидать события, а не ждать сообщения. B просто установит это событие вместо публикации сообщения.
Это работало довольно долго пока один конкретный компьютер не начал потерять случайное сообщение.
Не случайно ли количество процессоров или ядер, которые имеет данный компьютер, отличается от других, где вы не видите проблем? Иногда при переключении с компьютера с одним процессором на компьютер с более чем одним физическим процессором / ядром могут возникать новые условия гонки или взаимоблокировки.
Может ли быть второй экземпляр, который по незнанию работает и ест сообщения, помечая их как обработанные?