2012-03-14 12 views
19

Ho un'applicazione che deve rispondere a certi eventi nel modo seguente:Qual è il modo corretto di gestire gli eventi in C++?

void someMethodWithinSomeClass() { 
    while (true) { 
     wait for event; 
     if (event == SomeEvent) { 
      doSomething(); 
      continue; 
     } 
     if (event == SomeOtherEvent) { 
      doSomethingElse(); 
      continue; 
     } 
    } 
} 

Ciò sarebbe in esecuzione è qualche filo. In alcuni altri thread, le operazioni avrebbero creato e attivato gli eventi. Come faccio a ottenere questi eventi per raggiungere il metodo/la classe sopra indicati? Qual è la strategia o l'architettura appropriata per implementare la gestione degli eventi in C++?

+3

Il linguaggio C++ in realtà non hanno il supporto nativo per questo genere di cose. Dovrai utilizzare l'API per qualsiasi sistema operativo su cui stai lavorando. –

+3

In genere il C++ moderno utilizza segnali e slot (si veda [Boost.Signals2] (http://www.boost.org/libs/signals2/)) anziché passare il messaggio per gli eventi. L'approccio che stai mostrando è già arcaico, quindi il C++ non ha nulla di speciale da offrire come lingua per supportarlo. – ildjarn

+0

Effettua alcune ricerche per BlockingQueue. Il gestore bloccherà sulla coda get() fino a quando l'evento non verrà pubblicato nella coda. – Java42

risposta

9

Lo standard C++ non risolve affatto gli eventi. Di solito, tuttavia, se hai bisogno di eventi, stai lavorando all'interno di un framework che li fornisce (SDL, Windows, Qt, GNOME, ecc.) E modi di attendere, spedire e usarli.

A parte questo, si consiglia di guardare Boost.Signals2.

+5

Si noti che, sebbene Boost.Signals2 sia sicuro per i thread, non fornisce un meccanismo per inviare gli eventi di accodamento da un altro thread. –

5

C++ non ha il supporto integrato per eventi. Dovresti implementare una sorta di coda di attività thread-safe. Il thread di elaborazione dei messaggi principale estrae continuamente gli elementi da questa coda e li elabora.

Un buon esempio di questo è pompa messaggio Win32 standard che spinge applicazioni Windows:

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) 
{ 
    MSG msg; 
    while(GetMessage(&msg, NULL, 0, 0) > 0) 
    { 
    TranslateMessage(&msg); 
    DispatchMessage(&msg); 
    } 
    return msg.wParam; 
} 

Altre filettature può Post un messaggio ad una finestra, che verrà poi gestito da questo thread.

Questo utilizza C piuttosto che C++, ma illustra l'approccio.

6

C++ 11 e Boost hanno condition variables. Sono un mezzo per un thread per sbloccare un altro in attesa di qualche evento. Il link sopra ti porta alla documentazione per boost::condition_variable e ha un esempio di codice che mostra come usarlo.

Se è necessario tenere traccia degli eventi (ad esempio, sequenze di tasti) e devono elaborarli in modalità FIFO (first-in first-out), sarà necessario utilizzare o creare una sorta di multi-threading sistema di accodamento degli eventi, come suggerito in alcune delle altre risposte.

9

Spesso, code di evento sono implementati come command design pattern:

Nella programmazione orientata agli oggetti, il modello di comando è un disegno pattern in cui un oggetto viene utilizzato per rappresentare e incapsulare tutte le informazioni necessarie per chiamare un metodo in un secondo momento. Queste informazioni includono il nome del metodo, l'oggetto proprietario del metodo e i valori per i parametri del metodo.

In C++, l'oggetto che possiede il metodo ei valori per i parametri del metodo è un funtore nullaria (cioè un funtore che non accetta argomenti). Può essere creato utilizzando boost::bind() o C++11 lambdas e inserito in boost::function.

Ecco un esempio minimalista su come implementare una coda di eventi tra più produttori e più thread di consumo.Uso:

void consumer_thread_function(EventQueue::Ptr event_queue) 
try { 
    for(;;) { 
     EventQueue::Event event(event_queue->consume()); // get a new event 
     event(); // and invoke it 
    } 
} 
catch(EventQueue::Stopped&) { 
} 

void some_work(int n) { 
    std::cout << "thread " << boost::this_thread::get_id() << " : " << n << '\n'; 
    boost::this_thread::sleep(boost::get_system_time() + boost::posix_time::milliseconds(500)); 
} 

int main() 
{ 
    some_work(1); 

    // create an event queue that can be shared between multiple produces and multiple consumers 
    EventQueue::Ptr queue(new EventQueue); 

    // create two worker thread and pass them a pointer to queue 
    boost::thread worker_thread_1(consumer_thread_function, queue); 
    boost::thread worker_thread_2(consumer_thread_function, queue); 

    // tell the worker threads to do something 
    queue->produce(boost::bind(some_work, 2)); 
    queue->produce(boost::bind(some_work, 3)); 
    queue->produce(boost::bind(some_work, 4)); 

    // tell the queue to stop 
    queue->stop(true); 

    // wait till the workers thread stopped 
    worker_thread_2.join(); 
    worker_thread_1.join(); 

    some_work(5); 
} 

Uscite:

./test 
thread 0xa08030 : 1 
thread 0xa08d40 : 2 
thread 0xa08fc0 : 3 
thread 0xa08d40 : 4 
thread 0xa08030 : 5 

Implementazione:

#include <boost/function.hpp> 
#include <boost/thread/thread.hpp> 
#include <boost/thread/condition.hpp> 
#include <boost/thread/mutex.hpp> 
#include <boost/smart_ptr/intrusive_ptr.hpp> 
#include <boost/smart_ptr/detail/atomic_count.hpp> 
#include <iostream> 

class EventQueue 
{ 
public: 
    typedef boost::intrusive_ptr<EventQueue> Ptr; 
    typedef boost::function<void()> Event; // nullary functor 
    struct Stopped {}; 

    EventQueue() 
     : state_(STATE_READY) 
     , ref_count_(0) 
    {} 

    void produce(Event event) { 
     boost::mutex::scoped_lock lock(mtx_); 
     assert(STATE_READY == state_); 
     q_.push_back(event); 
     cnd_.notify_one(); 
    } 

    Event consume() { 
     boost::mutex::scoped_lock lock(mtx_); 
     while(STATE_READY == state_ && q_.empty()) 
      cnd_.wait(lock); 
     if(!q_.empty()) { 
      Event event(q_.front()); 
      q_.pop_front(); 
      return event; 
     } 
     // The queue has been stopped. Notify the waiting thread blocked in 
     // EventQueue::stop(true) (if any) that the queue is empty now. 
     cnd_.notify_all(); 
     throw Stopped(); 
    } 

    void stop(bool wait_completion) { 
     boost::mutex::scoped_lock lock(mtx_); 
     state_ = STATE_STOPPED; 
     cnd_.notify_all(); 
     if(wait_completion) { 
      // Wait till all events have been consumed. 
      while(!q_.empty()) 
       cnd_.wait(lock); 
     } 
     else { 
      // Cancel all pending events. 
      q_.clear(); 
     } 
    } 

private: 
    // Disable construction on the stack. Because the event queue can be shared between multiple 
    // producers and multiple consumers it must not be destroyed before the last reference to it 
    // is released. This is best done through using a thread-safe smart pointer with shared 
    // ownership semantics. Hence EventQueue must be allocated on the heap and held through 
    // smart pointer EventQueue::Ptr. 
    ~EventQueue() { 
     this->stop(false); 
    } 

    friend void intrusive_ptr_add_ref(EventQueue* p) { 
     ++p->ref_count_; 
    } 

    friend void intrusive_ptr_release(EventQueue* p) { 
     if(!--p->ref_count_) 
      delete p; 
    } 

    enum State { 
     STATE_READY, 
     STATE_STOPPED, 
    }; 

    typedef std::list<Event> Queue; 
    boost::mutex mtx_; 
    boost::condition_variable cnd_; 
    Queue q_; 
    State state_; 
    boost::detail::atomic_count ref_count_; 
}; 
+0

Non riesco a usare boost. Quali sono le mie opzioni per implementare la gestione degli eventi? – Tariq

+0

@Tariq Usa quindi 'std ::' equivalenti. –

+0

Mi spiace che non sia stato possibile modificare direttamente, ma penso che manchi una coppia di parentesi '{}' per racchiudere la funzione 'void consumer_thread_function (EventQueue :: Ptr event_queue)' (in cima alla maggior parte del codice). – gbmhunter

Problemi correlati