Подтвердить что ты не робот

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

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

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

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, но, похоже, это средство в сочетании с мьютексом, которое не здесь.

4b9b3361

Ответ 1

Я думаю, что хороший кросс-платформенный эквивалент событий 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();
}

Ответ 2

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

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 (что происходит, если два потока одновременно называют это, что происходит, если тот же поток вызывает это дважды). Однако решение будет очень похоже на это.

Ответ 3

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

#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);
}

Ответ 4

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

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

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

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

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

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

Ответ 5

вы можете использовать boost-поток barrier

#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();
}

Ответ 6

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

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

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

Ответ 7

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

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

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

Имейте foo опрос на логическом (да, это плохая идея.)

Ответ 8

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

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);
    }
}

Ответ 9

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

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

Ответ 10

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