Поведение, подобное CEvent, с Boost.Thread

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

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

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

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

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

Это означает, что между проверкой флагов и ожиданием 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?


person Kaz Dragon    schedule 30.08.2009    source источник


Ответы (1)


Как вы сказали, чтобы напоминать событие стиля 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-семафоры, кстати), заключается в том, что эти примитивы позволяют довольно легко облажаться. Если вы не планируете переносить свое приложение на другую платформу, адаптация вашего приложения к этому другому стилю может не стоить того.

person gimpf    schedule 30.08.2009