Кроссплатформенный эквивалент событий Windows

Я пытаюсь перенести некоторый код Windows в Linux, в идеале через независимые от платформы библиотеки (например, boost), однако я не уверен, как портировать этот бит кода события.

Часть кода включает два потока (давайте назовем их A и B). A хочет сделать что-то, что может сделать только B, поэтому он отправляет B сообщение, затем ждет, пока B скажет, что все сделано. В Windows это выглядит примерно так:

void foo();//thread a calls this
void bar(HANDLE evt);

void foo()
{
    HANDLE evt = CreateEvent(0,FALSE,FALSE,0);
    bCall(boost::bind(&bar, evt));
    WaitForSingleObject(evt,INFINITE);
    CloseHandle(evt);
}
void bar(HANDLE evt)
{
    doSomething();
    SetEvent(evt);
}

Я посмотрел на библиотеку boost::thread, но, похоже, у нее ничего не было, закрытие, которое я видел, было boost::condition_variable, но оказалось, что это означает в сочетании с мьютексом, что не так. Вот.

10 ответов

Решение

Я думаю, что хорошим, кросс-платформенным эквивалентом событий win32 является boost:: condition, поэтому ваш код может выглядеть примерно так:

void foo()
{
    boost::mutex mtxWait; 
    boost::condition cndSignal;

    bCall(boost::bind(&bar, mtxWait, cndSignal));

    boost::mutex::scoped_lock mtxWaitLock(mtxWait);
    cndSignal.wait(mtxWait); // you could also use cndSignal.timed_wait() here
}

void bar(boost::mutex& mtxWait, boost::condition& cndSignal)
{
    doSomething();
    cndSignal.notify_one();
}

Все эти ответы слишком сложны, давай люди, это не так сложно.

namespace porting
{
   class Event;
   typedef Event* Event_handle;
   static const unsigned k_INFINITE = 0xFFFFFFFF;

   class Event
   {
      friend Event_handle CreateEvent( void );
      friend void CloseHandle( Event_handle evt );
      friend void SetEvent( Event_handle evt );
      friend void WaitForSingleObject( Event_handle evt, unsigned timeout );

      Event( void ) : m_bool(false) { }

      bool m_bool;
      boost::mutex m_mutex;
      boost::condition m_condition;
   };

   Event_handle CreateEvent( void )
   { return new Event; }

   void CloseHandle( Event_handle evt )
   { delete evt; }

   void SetEvent( Event_handle evt )
   {
      evt->m_bool = true;
      evt->m_cond.notify_all();
   }

   void WaitForSingleObject( Event_handle evt, unsigned timeout )
   {
      boost::scoped_lock lock( evt->m_mutex );
      if( timeout == k_INFINITE )
      {
         while( !evt->m_bool )
         {
            evt->m_cond.wait( lock );
         }
      }
      else
      {
         //slightly more complex code for timeouts
      }
   }

}// porting

void foo()
{
   porting::Event_handle evt = porting::CreateEvent();
   bCall( boost::bind(&bar, evt ) );
   porting::WaitForSingleObject( evt, porting::k_INFINITE );
   porting::CloseHandle(evt);
}

void bar( porting::Event_handle evt )
{
   doSomething();
   porting::SetEvent(evt);
}

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

Вы можете использовать обещание и будущее, из темы повышения:

#include <boost\thread.hpp>

boost::promise<bool> prom;

void foo()
{
    auto future = prom.get_future();
    auto result = future.wait_for(boost::chrono::milliseconds(1000));
    // we get here if (a) 1 second passes or (b) bar sets the promise value
    if (result==boost::future_status::ready) 
    { 
        /* bar set the promise value */ 
    }
    if (result==boost::future_status::timeout)
    {
        /* 1 second passed without bar setting promise value */ 
    }
}

void bar()
{
    prom.set_value(true);
}

Для тех, кто занимается или работает над переносом многопоточного собственного кода Windows C/C++ на Linux/Mac, мы создали библиотеку с открытым исходным кодом (MIT-licensed), которая реализует как ручные, так и автоматические сброс событий WIN32 поверх pthreads, включая полную реализацию WaitForSingleObject а также WaitForMultipleObjectsчто делает его единственным из известных мне портов WFMO доступным в Linux / Mac.

pevents доступен на GitHub и был довольно проверен в бою и используется некоторыми громкими именами; есть также бустерный порт pevents, плавающий где-нибудь.

Использование pevents значительно упростит перенос кода из Windows, поскольку основные парадигмы для Windows и posix-платформ существенно отличаются - хотя я бы рекомендовал любому, кто пишет многоплатформенный код, использовать существующую кроссплатформенную многопоточную библиотеку, например, boost.

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

1) Есть проблема с решением @Alan. Предоставленный им пример кода работает хорошо. Но это отличается от функциональности Windows Events. Когда установлен объект события Windows, любое количество последующих вызовов WaitForSingleObject немедленно возвращается, показывая, что объект находится в сигнальном состоянии. Но с бустом mutex / condition решение, bar() должен уведомить условие для каждого foo() звонки, которые нужны. Это значительно усложняет ситуацию с "кроссплатформенностью" функциональности Windows Event. notify_all() тоже не могу помочь.

Конечно, это как-то решается в примере кода @deft_code с помощью логической переменной. (Хотя он страдает от проблемы состояния гонки. SetEvent(...) называется мертвым после while(!evt->m_bool) и раньше evt->m_cond.wait(lock) изнутри отдельной веткой. Будет тупик. Однако это можно решить с помощью некоторых методов управления состоянием гонки, чтобы сделать два утверждения while() а также wait() атомный.) Но у него есть свой недостаток:

2) Существует также проблема с кодом @deft_code при использовании boost mutex / condition / bool сочетание:

Объекты событий в Windows могут быть названы, что позволяет использовать их для синхронизации между процессами. Например, процесс A может создать именованное событие и установить его так: SetEvent(hFileIsReady), Впоследствии, любое количество процессов, ожидающих этого события, будет установлено (вызывая WaitForSingleObject(hFileIsReady)) немедленно продолжит свое обычное выполнение, пока событие не будет снова сброшено в процессе А путем ResetEvent(hFileIsReady),

Но комбинация mutex / condition / bool не могу позволить себе такую ​​функциональность. Конечно, мы можем использовать повышение named_condition а также named_mutex, Однако, как насчет логической переменной, которую мы должны проверить перед ожиданием?

Вы можете использовать бустер нитевой барьер

#include <boost/thread/thread.hpp>
#include <boost/thread/barrier.hpp>
#include <iostream>

void foo(boost::barrier* b)
{
  std::cout << "foo done" << std::endl;
  b->wait();
}


int main()
{
  std::cout << "start foo" << std::endl;
  boost::barrier b(2);

  boost::thread t(&foo, &b);
  b.wait();
  std::cout << "after foo done" <<  std::endl;
  t.join();
}

Я делал (или видел) все следующее в разное время для таких вещей:

Используйте мьютекс + условную переменную.

Используйте трубу, foo создайте трубу и передайте конец записи в bar. Бар тогда пишет в трубу, когда бар сделан. (Это даже работает многопроцессно).

Проведите foo poll на логическое значение (да, это плохая идея.)

Похоже, вы ищете механизм сигнального слота. Вы можете найти один в:

Boost и Qt

оба кроссплатформенные.

Пример Qt:

 #include <QObject>

 class Counter : public QObject
 {
     Q_OBJECT
 public:
     Counter() { m_value = 0; }

     int value() const { return m_value; }

 public slots:
     void setValue(int value);

 signals:
     void valueChanged(int newValue);

 private:
     int m_value;
 };

Counter a, b;
QObject::connect(&a, SIGNAL(valueChanged(int)),
                  &b, SLOT(setValue(int)));

a.setValue(12);     // a.value() == 12, b.value() == 12
b.setValue(48);     // a.value() == 12, b.value() == 48

void Counter::setValue(int value)
{
    if (value != m_value) {
        m_value = value;
        emit valueChanged(value);
    }
}

Из Boost.Thread версии 1.47 документации:

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

Под Posix-совместимыми системами вы можете использовать Posix IPC. Он используется для обмена сообщениями между процессами. Если я правильно помню, есть доступный порт Cygwin.

Другие вопросы по тегам