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