Équivalent multiplateforme aux événements windows
J'essaie de porter du code Windows vers Linux, Idéalement via des bibliothèques indépendantes de la plate-forme (par exemple boost), mais je ne sais pas comment porter ce peu de code d'événement.
Le bit de code implique deux threads (appelons-les A et B). A veut faire quelque chose que seul B peut, donc il envoie un message à B, puis attend que B dise que c'est fait. Dans windows, cela ressemble à quelque chose comme:
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);
}
J'ai regardé la bibliothèque boost::thread, mais il ne semblait pas avoir quelque chose qui fait cela, le ferme je pouvais voir était le boost::condition_variable, mais il semble que ce soit des moyens en conjonction avec un mutex, ce qui n'est pas le cas ici.
10 réponses
Je pense qu'un bon équivalent multiplateforme aux événements win32 est boost:: condition , donc votre code pourrait ressembler à ceci:
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();
}
Toutes ces réponses sont trop complexes, allez les gens, ce n'est pas si difficile.
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);
}
Il y a probablement un peu plus à faire pour que cela fonctionne pleinement car je ne suis pas familier avec la sémantique de WaitForSingleObject
(Que se passe-t-il si deux threads l'appellent en même temps, que se passe-t-il si le même thread l'appelle deux fois). Cependant, la solution ressemblera beaucoup à ceci.
Vous pouvez utiliser une promesse et un avenir, à partir de 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);
}
Comme les commentaires sont fermés pour moi, j'ai dû poster mes commentaires aux messages précédents comme réponse. Mais en réalité, je ne suis pas répondre.
1) Il y a un problème avec la solution de @Alan.
L'exemple de code fourni fonctionne bien. Mais il est différent de la fonctionnalité des événements Windows. Lorsqu'un objet événement Windows est défini, tout nombre d'appels ultérieurs à WaitForSingleObject
retourne immédiatement, montrant que l'objet est dans l'état signalé. Mais avec boost's mutex
/ condition
solution, {[3] } doit notifier la condition pour tous les appels foo()
qui en ont besoin. Cela rend la situation beaucoup plus difficile pour la fonctionnalité D'événement Windows "multi-plateforme". notify_all()
aussi ne peut pas aider.
Bien sûr, cela est résolu dans l'exemple de code de @deft_code en utilisant une variable booléenne. (Bien qu'il souffre lui-même de problème de condition de course. Considérez si SetEvent(...)
est appelé mort après while(!evt->m_bool)
et avant evt->m_cond.wait(lock)
à partir d'un thread séparé. Une impasse se produira. Cela peut cependant être résolu par en utilisant certaines techniques de gestion des conditions de concurrence pour faire les deux déclarations while()
et wait()
atomique.) Mais il a sa propre lacune:
2) Il y a aussi un problème avec @deft_code le code dans l'utilisation de boost mutex
/condition
/bool
combinaison:
Les objets D'événement dans Windows peuvent être nommés ce qui leur permet d'être utilisés pour les synchronisations inter-processus. Par exemple, le processus A peut créer un événement nommé et le Définir comme ceci: SetEvent(hFileIsReady)
. Après, peu importe nombre de processus en attente de cet événement (appelant ainsi WaitForSingleObject(hFileIsReady)
) continueront immédiatement leur exécution normale jusqu'à ce que l'événement soit à nouveau réinitialisé dans le processus A par ResetEvent(hFileIsReady)
.
Mais la combinaison mutex
/condition
/bool
Je ne peux pas me permettre une telle fonctionnalité. Bien sûr, nous pouvons utiliser boost named_condition
et named_mutex
. Cependant, qu'en est-il de la variable booléenne que nous devons vérifier avant d'attendre?
Vous pouvez utiliser boost thread barrière
#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();
}
Pour toute personne impliquée ou travaillant sur le portage de Code Windows c/c++ natif multithread vers Linux / Mac, nous avons créé une bibliothèque open source (sous licence MIT) {[4] } qui implémente les événements WIN32 à réinitialisation manuelle et automatique sur pthreads, y compris une implémentation complète de WaitForSingleObject
et WaitForMultipleObjects
, ce qui en fait le seul port WFMO
Pevents est disponible sur GitHub et a été assez testé au combat et est utilisé par certains grands noms; il y a aussi un port boost de pevents flottant quelque part.
L'utilisation de pevents facilitera considérablement le portage du code à partir de Windows, car les paradigmes sous - jacents sont radicalement différents entre les plates-formes Windows et posix-bien que j'encourage quiconque écrit du code multi-plateforme à utiliser une bibliothèque multithreading multiplateforme existante comme boost.
J'ai fait (ou vu) tout ce qui suit à différents moments pour des choses comme ceci:
Utilise un mutex + une variable de condition.
Utilisez un tuyau, ayant foo créer le tuyau et passer l'extrémité d'écriture de celui-ci à la barre. La barre écrit alors dans le tuyau quand la barre est faite. (Cela fonctionne même multi-processus).
Avoir un sondage foo sur un booléen (Oui, c'est une mauvaise idée.)
On dirait que vous cherchez mechanizm signal slot. Vous pouvez en trouver un dans:
Les deux crossplatform.
Qt exemple:
#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);
}
}
De Boost.Version du Thread 1.47 documentation :
Les classes condition_variable et condition_variable_any fournissent un mécanisme pour un thread à attendre l'avis d'un autre thread qu'une condition particulière est devenue vraie.
Sous les systèmes compatibles Posix, vous pouvez utiliser Posix IPC. Il est utilisé pour la messagerie inter-processus / inter-thread. Si je me souviens bien, il y a un port cygwin disponible.