2012-03-14 46 views
19

que tienen una aplicación que necesita para responder a ciertos eventos de la siguiente manera:¿Cuál es la forma correcta de manejar eventos en C++?

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

Esto estaría funcionando es un poco de hilo. En algunos otros hilos, las operaciones crearían y dispararían los Eventos. ¿Cómo logro que estos eventos lleguen al método/clase anterior? ¿Cuál es la estrategia o arquitectura adecuada para implementar el manejo de eventos en C++?

+3

El lenguaje C++ realmente no tiene soporte nativo para este tipo de cosas. Deberá usar la API para cualquier sistema operativo en el que esté trabajando. –

+3

En general, C++ moderno usa señales y ranuras (ver [Boost.Signals2] (http://www.boost.org/libs/signals2/)) en lugar de mensaje que pasa para eventos. El enfoque que está mostrando ya es arcaico, por lo que C++ no tiene nada especial que ofrecer como idioma para apoyarlo. – ildjarn

+0

Realice algunas búsquedas de BlockingQueue. El controlador bloqueará en cola get() hasta que el evento se publique en la cola. – Java42

Respuesta

9

El estándar C++ no aborda eventos en absoluto. Por lo general, sin embargo, si necesita eventos, está trabajando dentro de un marco que los proporciona (SDL, Windows, Qt, GNOME, etc.) y formas de esperarlos, despacharlos y usarlos.

Aparte de eso, es posible que desee consultar Boost.Signals2.

+5

Tenga en cuenta que, si bien Boost.Signals2 es seguro para subprocesos, no proporciona un mecanismo para cola de eventos para ser enviados por otro subproceso. –

5

C++ no tiene soporte integrado para eventos. Tendría que implementar algún tipo de cola de tareas segura para subprocesos. Su hilo principal de procesamiento de mensajes extraería continuamente elementos de esta cola y los procesaría.

Un buen ejemplo de esto es bomba estándar mensaje Win32 que impulsa las aplicaciones de 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; 
} 

Otros hilos pueden Post un mensaje a una ventana, que luego será manejada por este hilo.

Esto utiliza C en lugar de C++, pero ilustra el enfoque.

6

C++ 11 y Boost tienen condition variables. Son un medio para que un hilo desbloquee a otro que está esperando que ocurra algún evento. El enlace de arriba lo lleva a la documentación para boost::condition_variable, y tiene un ejemplo de código que muestra cómo usarlo.

Si necesita realizar un seguimiento de los eventos (por ejemplo, pulsaciones de teclas) y necesita procesarlos de una manera FIFO (primero en entrar, primero en salir), entonces tendrá que usar o hacer algún tipo de subprocesos múltiples sistema de cola de eventos, como se sugiere en algunas de las otras respuestas.

9

A menudo, las colas de eventos se implementan como command design pattern:

En la programación orientada a objetos, el patrón de comandos es una patrón de diseño en el que se utiliza un objeto para representar y encapsular todos la información necesaria para llamar un método en un momento posterior. Esta información incluye el nombre del método, el objeto que posee el método y los valores de los parámetros del método.

En C++, el objeto que posee el método y los valores para los parámetros del método es un funtor nullary (es decir un funtor que no toma argumentos). Se puede crear usando boost::bind() o C++11 lambdas y envuelto en boost::function.

Aquí hay un ejemplo minimalista sobre cómo implementar una cola de eventos entre varios hilos de productor y múltiples de consumidor.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); 
} 

Salidas:

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

Implementación:

#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

No puedo usar boost. ¿Cuáles son mis opciones para implementar el manejo de eventos? – Tariq

+0

@Tariq Usa los equivalentes 'std ::' entonces. –

+0

Lo siento, no pude editar directamente, pero creo que te falta un par de corchetes '{}' para adjuntar la función 'void consumer_thread_function (EventQueue :: Ptr event_queue)' (la parte más alta del código). – gbmhunter

Cuestiones relacionadas