2009-11-04 5 views
22

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 suis pas sûr de savoir comment porter ce bit de code d'événement.Équivalent multiplateforme pour les événements Windows

Le bit de code implique deux threads (appelons-les A et B). A veut faire quelque chose que seul B peut faire, donc il envoie un message à B, puis attend que B dise que c'est fait. Dans les fenêtres 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); 
} 

Je regardais le boost :: bibliothèque de fil, mais il na pas semblent avoir quelque chose qui fait cela, les ferme que je pouvais voir était le boost :: condition_variable, mais il semble c'est à dire en conjonction avec un mutex, ce qui n'est pas le cas ici.

+0

Je pense que votre code spécifique à Windows utilise un mutex sous le capot. Cela résume juste cela. – rmeador

+0

duplication possible de [événement de réinitialisation manuelle de fenêtres pthread-like] (http://stackoverflow.com/questions/178114/pthread-like-windows-manual-reset-event) – jww

+0

[cette question] (http: // stackoverflow .com/questions/4692717/win32-reset-événement-comme-synchronisation-classe-avec-boost-c) a aussi une bonne info – Loomchild

Répondre

10

Je pense qu'une bonne, ce qui équivaut multiplateformes aux événements win32 est boost::condition, de sorte que 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(); 
} 
+1

Le mutex est-il vraiment nécessaire pour cela? J'ai certainement un point comme le fait que B pourrait potentiellement bloquer dans la barre (si la tranche de temps du thread A a expiré entre mtxWaitLock et cndSignal.wait). –

+0

le deuxième verrou mutex n'est pas nécessaire. Vous n'avez pas besoin de tenir le mutex pour appeler notify (il y a des situations où vous devriez le faire). –

+0

oui vous avez raison - je vais modifier le code – Alan

2

vous pouvez utiliser du fil 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(); 
} 
+1

Cela fonctionne dans certains cas, mais la barrière n'a pas de valeur de délai d'attente qui est utilisé dans un couple de des endroits. –

-1

Dans 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.

0

Je l'ai fait (ou vu) toutes les conditions suivantes à plusieurs reprises pour des choses comme ceci:

Utilisez un mutex + une variable de condition.

Utilisez un tube, en créant le tube et en lui transmettant l'extrémité d'écriture. Bar écrit alors dans le tuyau lorsque la barre est terminée. (Cela fonctionne même multi-processus).

Avez sondage foo sur un booléen (oui, cela est une mauvaise idée.)

0

On dirait que vous cherchez mechanizm signal fente. Vous pouvez en trouver un dans:

boost et Qt

deux multiplate.

exemple 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); 
    } 
} 
+1

Je ne pense pas que le mécanisme boost/slot ait un mécanisme d'attente quelconque –

+0

Mais avec le signal/slot je ne vois pas de raison d'attendre()? Si quelque chose est prêt, il suffit de publier le message, tout client intéressé procéderait une action appropriée. Modèle de dessin d'observateur simple. – bua

+0

@bua L'implémentation de la méthode waitForReadyRead est au moins une raison d'attendre. Dans ce cas, vous devez attendre les données et vous ne devez pas bloquer les événements QT. – baltazar

14

Toutes ces réponses sont trop complexe, 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 obtenir ce travail tout à fait que je ne suis pas familier avec la sémantique de WaitForSingleObject (ce qui se produit si deux threads appellent en même temps, ce qui se passe si le même fil appelle deux fois). Cependant, la solution ressemblera beaucoup à ceci.

0

Depuis la version 1.47 Boost.Thread documentation:

Les classes condition_variable et condition_variable_any fournissent un mécanisme pour un thread d'attendre la notification d'un autre thread qu'une condition particulière est vrai.Puisque les commentaires sont fermés pour moi, j'ai dû poster mes commentaires aux poteaux précédents en tant que réponse.

4

Mais en fait je ne réponds pas.

1) Il y a un problème avec la solution de @Alan. L'exemple de code qu'il a fourni fonctionne bien. Mais il est différent de la fonctionnalité Windows Events. Lorsqu'un objet événement Windows est défini, un nombre quelconque de appels suivants à WaitForSingleObject renvoie immédiatement, indiquant que l'objet est dans l'état signalé. Mais avec la solution mutex/condition de Boost, bar() doit notifier la condition pour tous les appels foo() qui en ont besoin. Cela rend la situation beaucoup plus difficile pour les fonctionnalités de Windows Event «multiplateformes». notify_all() ne peut pas non plus 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 subisse lui-même un problème de condition de concurrence, considérer si SetEvent(...) est appelé cadavre après while(!evt->m_bool) et avant evt->m_cond.wait(lock) à partir d'un thread séparé.Un blocage se produira, mais peut être résolu en utilisant certaines techniques de gestion des conditions de course 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 le code de l » @deft_code en faisant usage de stimuler mutex/condition/bool combinaison:

objets d'événements dans Windows peut être nommé 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 suit: SetEvent(hFileIsReady). Par la suite, quel que soit le nombre de processus en attente pour que cet événement soit défini (appelant ainsi WaitForSingleObject(hFileIsReady)) continuera immédiatement leur exécution normale jusqu'à ce que l'événement soit de nouveau réinitialisé dans le processus A par ResetEvent(hFileIsReady).

Mais la combinaison mutex/condition/bool ne peut pas 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?

8

Vous pouvez utiliser une promesse et un avenir, de stimuler fil:

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

Pour toute personne impliquée ou travaillant sur le portage multi-thread Windows code C/C++ natif pour Linux/Mac, we've authored an open source (MIT-licensed) library qui implémente les deux les événements WIN32 manuels et à réinitialisation automatique au-dessus de pthreads, y compris une implémentation complète de WaitForSingleObject et WaitForMultipleObjects, ce qui en fait le seul port WFMO que je connaisse sur Linux/Mac.

pebents is available on GitHub et a été assez testé et est utilisé par de grands noms; il y a aussi un port de pevents qui flotte quelque part. L'utilisation de pevents facilitera considérablement le portage de Windows car les paradigmes sous-jacents sont très différents entre Windows et les plates-formes posix - bien que j'encourage tous ceux qui écrivent du code multiplateforme à utiliser une bibliothèque multithread multiplateforme existante comme boost dans la première place.

+0

Merci! J'ai été à la recherche d'une solution portable WFMO pour les âges. C'est dommage que la norme C++ ne porte rien de proche, AFAIK, boost non plus. J'ai certainement un usage immédiat pour votre bibliothèque. –

Questions connexes