Bezdepbonusu7Программирование: статьи и ответы

Вопрос

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

Автор вопроса: Fire LancerОтветов: 10

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

  • Я думаю, что ваш специфичный для Windows код использует мьютекс под капотом. Он просто абстрагируется от этого. rmeador 05.11.2009
  • возможный дубликат события ручного сброса Windows, похожего на pthread jww 11.02.2014
  • этот вопрос также содержит полезную информацию Loomchild 17.01.2017

windowsc++multithreadingboostcross-platform

Ответы

Ответ 1 · рейтинг 10 · Alan

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

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

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 · рейтинг 8 · oofafu

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

#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 · рейтинг 4 · Masood Khaari

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

1) Проблема с решением @Alan. Предоставленный им пример кода работает хорошо. Но это отличается от функций Windows Events. Когда объект события Windows установлен, немедленно возвращается любое количество последующих вызовов WaitForSingleObject, показывая, что объект находится в сигнальном состоянии. Но с решением boost mutex/condition 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 могут иметь имя, что позволяет использовать их для межпроцессной синхронизации. Например, процесс А может создать именованное событие и установить его следующим образом: SetEvent(hFileIsReady). После этого независимо от количества процессов, ожидающих установки этого события (таким образом вызывая WaitForSingleObject(hFileIsReady)), немедленно продолжит свое обычное выполнение до тех пор, пока событие не будет снова сброшено в процессе A ResetEvent(hFileIsReady).

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

Ответ 5 · рейтинг 3 · Mahmoud Al-Qudsi

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

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

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

Ответ 6 · рейтинг 2 · chub

вы можете использовать барьер потока повышения

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

Ответ 7 · Michael Kohne

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

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

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

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

Ответ 8 · bua

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

повышение и 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 · Jean Davy

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

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

Ответ 10 · рейтинг -1 · Elmar Weber

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

Перевод вопроса и ответов сообщества Stack Overflow, лицензия CC BY-SA. Оригинал на Stack Overflow.

Похожие материалы