2012-03-14 198 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

相關問題