CEvent-подобное поведение с Boost.Thread - PullRequest
3 голосов
/ 30 августа 2009

Проблема в словах:

Для моего приложения у меня есть класс, который читает с последовательного порта. Он использует примитивы Windows для обработки COM-порта и имеет поток для асинхронного чтения. Я пытаюсь преобразовать это из примитивов Windows, используя библиотеки Boost, такие как Boost.Asio и Boost.Thread.

В порте Windows мой поток ввода-вывода содержал несколько переменных MFC CEvent, каждая из которых представляла сообщение: запрошено чтение, запрошено запись, выполнено чтение, запись завершена, IO отменен. Их ждали с WaitForMultipleObjects.

Проблема в том, что Boost.Thread не имеет аналогов ни для CEvent, ни для WaitForMultipleObjects. Самое близкое, что я пришел, - это отбросить их и заменить события набором логических значений, а затем использовать условие condition_variable, которое вызывает свою функцию notify_all () при каждом изменении логического значения.

Тем не менее, boost :: condition_variable отличается одним критическим образом от CEvent: если CEvent сигнализируется, когда его не ожидают, то следующее ожидание немедленно завершается успешно. С boost :: condition_variable любая функция уведомления игнорируется, если она не ожидает.

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

Кто-нибудь знает решение этой проблемы?

Проблема в коде:

// Old IO Thread
CEvent msg_cancel;
CEvent msg_read_req;
CEvent msg_write_req;
CEvent msg_read_comp;
CEvent msg_write_comp;

CEvent events[] = { 
    msg_cancel, 
    msg_read_req, 
    msg_write_req,
    msg_read_comp,
    msg_write_comp
};

bool cancel = false;

while (!cancel)
{
    switch(WaitForMultipleObjects(5, events, false, INFINITE))
    {
        case WAIT_OBJECT_0 :
            // msg_cancel
            cancel = true;
            break;

        ...
     }
}

Как эмулировать это в Boost.Thread?

1 Ответ

3 голосов
/ 30 августа 2009

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

Однако проблема, о которой вы упоминали (условные переменные никогда не получают состояние active, когда ожидание немедленно вернется), обычно решается следующим образом:

condition-variable
mutex

main-thread:
  lock(mutex) { start condition-signaling-thread }
  while(some predicate) {
    condition-variable.wait(mutex)
    do-stuff
  }

condition-signaling-thread:
  loop:      
    lock(mutex) {
      do-whatever
    }
    condition-variable.notify();

Имея второй поток, ожидающий, пока мьютекс не будет разблокирован потоком, который будет обрабатывать условие, вы можете обеспечить выполнение каждого условия. (Примечание: в Java метод notify () должен вызываться внутри блокировки, что, в зависимости от деталей реализации, может привести к снижению производительности, если это делается в C ++, но гарантирует, что программист хотя бы однажды задумался о том, как синхронизировать стрельба из состояния с получателем).

Причина, по которой boost.thread не предоставляет события в стиле Windows (и posix-семафоры, кстати), заключается в том, что эти примитивы довольно легко испортить. Если вы не планируете переносить ваше приложение на другую платформу, адаптация вашего приложения к этому другому стилю может не стоить этого.

...