2009-11-04 17 views
22

Sto provando a portare alcuni codici Windows su Linux, idealmente attraverso librerie indipendenti dalla piattaforma (es. Boost), tuttavia non sono sicuro di come trasferire questo bit del codice evento.Cross-Platform equivalente a eventi Windows

Il bit di codice prevede due thread (chiamiamoli A e B). A vuole fare qualcosa che solo B può, quindi invia un messaggio a B, quindi aspetta che B dica che è stato fatto. In Windows Questo sembra qualcosa di simile:

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

ho guardato la biblioteca boost :: thread, ma che non ha ancora sembrano avere tutto ciò che fa questo, si chiude ho potuto vedere è stato il boost :: condition_variable, ma sembra questo è un mezzo in congiunzione con un mutex, che non è il caso qui.

+0

Penso che il codice specifico per Windows stia utilizzando un mutex sotto il cofano. Lo astrae solo via. – rmeador

+0

possibile duplicato di [evento di reimpostazione manuale di windows simile a pthread]] (http://stackoverflow.com/questions/178114/pthread-like-windows-manual-reset-event) – jww

+0

[questa domanda] (http: // stackoverflow .com/questions/4692717/win32-reset-event-like-synchronization-class-with-boost-c) ha anche una bella informazione – Loomchild

risposta

10

penso che una buona, cross-platform equivalente a eventi Win32 è boost::condition, così il vostro codice potrebbe essere simile a questa:

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

Il mutex è davvero necessario per questo? Certamente mi piace il fatto che significa che B potrebbe potenzialmente bloccare in bar (se la fascia temporale del thread A è scaduta tra mtxWaitLock e cndSignal.wait). –

+0

non è necessario il secondo blocco mutex. Non è necessario tenere premuto il mutex per chiamare la notifica (ci sono situazioni in cui è necessario). –

+0

sì hai ragione - modifico il codice – Alan

2

è possibile utilizzare filo di spinta 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

Che funziona in alcuni casi, tuttavia la barriera manca di un valore di timeout che viene utilizzato in un paio di posti. –

-1

Sotto sistemi conformi Posix è possibile utilizzare Posix IPC. Viene utilizzato per la messaggistica tra processi e inter-thread. Se ricordo bene, c'è una porta cygwin disponibile.

0

che ho fatto (o visto) tutti i seguenti elementi in tempi diversi per cose come questa:

Usare un mutex + una variabile di condizione.

Utilizzare un tubo, avendo foo creare il tubo e passare la fine di scrittura di esso a barra. La barra quindi scrive nel tubo quando la barra è completata. (Funziona anche multi-processo).

Avere foo sondaggio su un valore booleano (sì, questa è una cattiva idea.)

0

Sembra che tu stia cercando di segnale slot mechanizm. È possibile trovare uno in:

boost e Qt

entrambi multipiattaforma.

esempio 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

Non credo che il meccanismo boost/slot abbia qualche tipo di meccanismo di attesa –

+0

Ma con segnale/slot non vedo un motivo per aspettare()? Se qualcosa è pronto, basta pubblicare il messaggio, qualsiasi cliente interessato procederà all'azione appropriata. Schema di disegno osservatore semplice. – bua

+0

@bua L'implementazione del metodo 'waitForReadyRead' è almeno un motivo per attendere. In questo caso è necessario attendere i dati e non bloccare gli eventi QT. – baltazar

14

Tutte queste risposte sono troppo complesso, andiamo gente non è così 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); 
} 

C'è probabilmente un po 'più da fare per ottenere questo completamente funzionante come io non sono a conoscenza la semantica di WaitForSingleObject (cosa succede se due thread chiamano allo stesso tempo, che cosa succede se lo stesso filo lo chiama due volte). Tuttavia, la soluzione sembrerà molto simile a questa.

0

Da Boost.Thread versione 1.47 documentation:

Le classi condition_variable e condition_variable_any forniscono un meccanismo per un thread attendere la notifica di un altro filo che una condizione particolare, è diventato vero.

4

Poiché i commenti sono chiusi per me, ho dovuto postare i miei commenti ai post precedenti come risposta. Ma in realtà non sto rispondendo.

1) C'è un problema con la soluzione di @Alan. Il codice di esempio che ha fornito funziona bene. Ma è diverso dalla funzionalità di eventi di Windows. Quando un oggetto di Windows Event è insieme, numero qualsiasi di chiamate successive a WaitForSingleObject restituisce immediatamente, dimostrando che l'oggetto è in stato segnalato. Ma con la soluzione di spinta mutex/condition, bar() deve notificare la condizione per ogni foo() chiamate che ne hanno bisogno. Ciò rende molto più difficile la situazione per la funzionalità di eventi Windows "multipiattaforma". Anche notify_all() non può aiutare.

Ovviamente questo è in qualche modo risolto nel codice di esempio di @ deft_code utilizzando una variabile booleana. (Anche se soffre di problemi di condizioni di gara, considerare SetEvent(...) morto dopo while(!evt->m_bool) e prima di da un thread separato.) Si verificherà un deadlock, che tuttavia può essere risolto utilizzando alcune tecniche di gestione delle condizioni di gara per rendere le due dichiarazioni while() e wait() atomica) Ma ha il suo difetto:.

2) C'è anche un problema con il codice di 's @deft_code nel fare uso di spinta mutex/condition/bool combinazione:

oggetti eventi in Windows può essere denominato che consente di utilizzarli per le sincronizzazioni tra processi. Ad esempio, il processo A può creare un evento denominato e impostarlo in questo modo: SetEvent(hFileIsReady). In seguito, qualunque sia il numero di processi in attesa di questo evento da impostare (chiamando così WaitForSingleObject(hFileIsReady)) immediatamente continuare la loro normale esecuzione fino a quando l'evento viene di nuovo azzerato entro Un processo per ResetEvent(hFileIsReady).

Ma la combinazione mutex/condition/bool non può permettersi un tale funzionalità. Naturalmente, possiamo usare boost named_condition e named_mutex. Tuttavia, che dire della variabile booleana che dobbiamo controllare prima di aspettare?

8

Si potrebbe utilizzare una promessa e un futuro, a partire da fili spinta:

#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

per chi si occupa o lavorando sul porting multi-threaded codice nativo di Windows C/C++ per Linux/Mac, we've authored an open source (MIT-licensed) library che implementa sia reindirizzamento manuale e automatico degli eventi WIN32 in cima a pthreads, compresa un'implementazione completa di WaitForSingleObject e WaitForMultipleObjects, rendendola l'unica porta WFMO che conosco disponibile su Linux/Mac.

pevents is available on GitHub ed è stato abbastanza battaglia-testato ed è in uso da parte di alcuni grandi nomi; c'è anche un boost port di pevents che fluttuano da qualche parte.

L'utilizzo di pevents renderà il codice di porting da Windows notevolmente più semplice in quanto i paradigmi sottostanti sono radicalmente diversi tra Windows e le piattaforme posix - sebbene incoraggerei chiunque a scrivere codice multi-piattaforma per utilizzare una libreria multithreading esistente multipiattaforma come boost in il primo posto.

+0

Grazie !! Sono stato alla ricerca di una soluzione WFMO portatile per anni. E 'un peccato che lo standard C++ non porti nulla di simile, AFAIK, neanche boost. Ho sicuramente un uso immediato per la tua libreria. –