Saltar al contenido

¿Cuál es la forma correcta de manejar eventos en C++?

Tenemos la mejor respuesta que hallamos en línea. Nosotros queremos que te resulte útil y si puedes compartir algo que nos pueda ayudar a crecer hazlo con total libertad.

Solución:

A menudo, las colas de eventos se implementan como un patrón de diseño de comandos:

En la programación orientada a objetos, el patrón de comando es un patrón de diseño en el que se utiliza un objeto para representar y encapsular toda la información necesaria para llamar a 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 nulo (es decir, un funtor que no acepta argumentos). Se puede crear usando boost::bind() o C++11 lambdas y envuelto en boost::function.

Aquí hay un ejemplo minimalista de cómo implementar una cola de eventos entre múltiples subprocesos de productores y múltiples consumidores. 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 
#include 
#include 
#include 
#include 
#include 
#include 

class EventQueue

public:
    typedef boost::intrusive_ptr Ptr;
    typedef boost::function 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 Queue;
    boost::mutex mtx_;
    boost::condition_variable cnd_;
    Queue q_;
    State state_;
    boost::detail::atomic_count ref_count_;
;

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

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

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

Si necesita realizar un seguimiento de los eventos (por ejemplo, las pulsaciones de teclas) y necesita procesarlos de manera FIFO (primero en entrar, primero en salir), entonces tendrá que usar o crear algún tipo de sistema de cola de eventos de subprocesos múltiples, como se sugiere en algunas de las otras respuestas. Las variables de condición se pueden usar como bloques de construcción para escribir su propia cola de productor/consumidor, si elige no usar una implementación existente.

valoraciones y reseñas

Te invitamos a confirmar nuestro trabajo dejando un comentario y dejando una puntuación te damos la bienvenida.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *