2012-03-14 156 views
19

我有一个需要以以下方式某些事件做出响应的应用程序:在C++中做事件处理的正确方法是什么?

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

这将运行一些线索。在其他一些线程中,操作会创建并触发事件。如何让这些活动达到上述方法/课程?在C++中实现事件处理的正确策略或体系结构是什么?

+3

C++语言并没有真正有这种事情的原生支持。你需要使用API​​来处理你正在工作的任何操作系统。 – 2012-03-14 22:43:32

+3

通常现代C++使用信号和插槽(请参阅[Boost.Signals2](http://www.boost.org/libs/signals2/)),而不是事件的消息传递。你所展示的方法已经过时了,所以C++没有什么特别的东西可以作为支持它的语言。 – ildjarn 2012-03-14 22:51:00

+0

做一些搜索BlockingQueue。处理程序将阻塞队列get(),直到事件发布到队列。 – Java42 2012-03-14 22:51:53

回答

9

C++标准根本没有解决事件。然而,通常情况下,如果您需要在提供它们的框架(SDL,Windows,Qt,GNOME等)中使用的事件以及等待,调度和使用它们的方法。

除此之外,你可能想看看Boost.Signals2

+5

请注意,虽然Boost.Signals2是线程安全的,但它不提供排队事件的机制,以便由另一个线程分派。 – 2012-03-14 23:11:59

5

C++没有内置的事件支持。你将不得不实现某种线程安全的任务队列。你的主要消息处理线程将不断从这个队列中取出项目并处理它们。

的一个很好的例子是标准的Win32消息泵驱动窗口的应用程序:

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

其他线程可以Post消息到一个窗口,这将随后通过该线程来处理。

这使用C而不是C++,但它说明了这种方法。

6

C++ 11和Boost有condition variables。它们是一个线程去阻止另一个正在等待某个事件发生的方法。上面的链接将带您访问boost::condition_variable的文档,并有一个代码示例显示如何使用它。

如果您需要跟踪事件(例如击键)并需要以FIFO(先进先出)方式处理它们,那么您必须使用或制作某种多线程事件排队系统,正如一些其他答案中所建议的那样。

9

通常,事件队列被实现为command design pattern

在面向对象的编程中,该命令图案是设计 图案,其中一个目的是用来表示和封装所有 调用所需的信息稍后的方法。此信息包括方法名称,拥有方法 的对象以及方法参数的值。

在C++中,那些拥有方法参数的方法和值的对象是零元函子(即仿函数,它没有参数)。它可以使用boost::bind()C++11 lambdas创建并包装成boost::function

这是一个极简主义的例子,介绍如何在多个生产者和多个消费者线程之间实现一个事件队列。用法:

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

输出:

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

实现:

#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

我无法使用提升。我有什么选择来实现事件处理? – Tariq 2015-04-23 10:19:33

+0

@Tariq然后使用'std ::'等价物。 – 2015-04-23 10:20:29

+0

对不起,我不能直接编辑,但我想你错过了一对'{}'括号来包含'void consumer_thread_function(EventQueue :: Ptr event_queue)'函数(最上面的部分代码)。 – gbmhunter 2017-04-27 16:18:50

相关问题