2013-08-06 30 views
2

我怎樣才能取消已經發布的回調:如何取消升壓ASIO io_service對象後

getIoService()->post(boost::bind(&MyClass::myCallback, this)); 

,並保持其他貼回調不變?

問題是我有一些對象接收來自不同線程的事件,我將它們發佈到ioservice以便處理主線程中的事件。如果在某些時候我想刪除我的對象 - ioservice會嘗試在已銷燬的對象中執行已發佈的回調。在這種情況下,我不能在對象中存儲任何標誌,因爲它將被刪除。

有一個可能的解決方案,使用enable_shared_from_thisshared_from_this(),但想知道是否另一種解決方案。

由於

回答

2

通過io_service不能有選擇地取消以這樣的方式回調。一種選擇是將邏輯移至更高級別,例如MyClass。示例實現可以是:

class MyClass : public boost::enable_shared_from_this<MyClass> 
{ 
public: 
    typedef boost::shared_ptr<MyClas> Ptr; 
    static Ptr create(boost::asio::io_service& io_service) { 
     const Ptr result(new MyClass); 
     io_service.post(boost::bind(&MyClass::myCallback, result)); 
     return result; 
    } 

    void myCallback() { 
     if (_canceled) return; 
    } 

    void cancel() { _canceled = true; } 

private: 
    MyClass() : _canceled(false) { } 

private: 
    bool _canceled; 
}; 

該類使用boost::shared_ptr來強制執行共享所有權語義。這樣做保證對象的生命週期將一直存在,只要回撥在發送之前保留在io_service隊列中。

+0

好吧,我看,那麼如何刪除所有回調? – Artem

+0

問題是我有一些對象接收來自不同線程的事件,並將它們發佈到ioservice以處理主線程中的事件。如果在某些時候我想刪除我的對象 - ioservice會嘗試在已銷燬的對象中執行已發佈的回調。在這種情況下,我不能在對象中存儲任何標誌,因爲它將被刪除。 – Artem

+0

@Artem請用這個新信息編輯你的問題,你沒有提到你的原始問題中的線程或刪除對象。 –

5

正如Sam回答的那樣,不可能有選擇地取消發佈的處理程序。

如果目標是防止對其生命週期已過期的對象調用成員函數,那麼使用enable_shared_from_this是慣用的解決方案。這種方法的一個結果是對象的生命週期至少延長到處理程序的壽命。如果對象的析構函數可以被延遲,那麼考慮通過shared_from_this()將對象綁定到處理程序。另一方面,如果需要立即銷燬,那麼可以考慮編寫一個與實例弱結合的函子。 This問題討論綁定到weak_ptr,並提供一些研究/討論鏈接。下面是弱結合到物體上的函子的簡化的完整的例子:

#include <iostream> 
#include <boost/asio.hpp> 
#include <boost/bind.hpp> 
#include <boost/enable_shared_from_this.hpp> 
#include <boost/make_shared.hpp> 
#include <boost/shared_ptr.hpp> 

/// @brief Mocked up type. 
class MyClass: 
    public boost::enable_shared_from_this<MyClass> 
{ 
public: 
    MyClass()  { std::cout << "MyClass()"   << std::endl; } 
    ~MyClass() { std::cout << "~MyClass()"  << std::endl; } 
    void action() { std::cout << "MyClass::action()" << std::endl; } 
}; 

/// @brief weak_binder is a functor that binds a member function 
///  to a weakly managed object instance. I.e. this 
///  functor will not extend the life of the instance to 
///  which it has been bound. 
template <typename Fn, 
      typename C> 
struct weak_binder 
{ 
private: 
    typedef typename C::element_type element_type; 
public: 

    /// @brief Constructor. 
    weak_binder(Fn& fn, C& c) : fn_(fn), c_(c) 
    {} 

    /// @brief Conditional invoke Fn if C still exists. 
    void operator()() 
    { 
    std::cout << "weak_binder::operator()" << std::endl; 
    // Create a shared pointer from the weak pointer. If 
    // succesful, then the object is still alive. 
    if (boost::shared_ptr<element_type> ptr = c_.lock()) 
    { 
     // Invoke the function on the object. 
     (*ptr.*fn_)(); 
    } 
    } 
private: 
    Fn fn_; 
    boost::weak_ptr<element_type> c_; 
}; 

/// @brief Helper function to create a functor that weakly 
///  binds to a shared object. 
template <typename Fn, 
      typename C> 
weak_binder<Fn, C> weak_bind(Fn fn, C c) 
{ 
    return weak_binder<Fn, C>(fn, c); 
} 

int main() 
{ 
    boost::asio::io_service io_service; 
    boost::shared_ptr<MyClass> my_class = boost::make_shared<MyClass>(); 

    // my_class will remain alive for this handler because a shared_ptr 
    // is bound to handler B, and handler B will only be destroyed after 
    // handler A has been destroyed. 
    io_service.post(weak_bind(&MyClass::action, 
          my_class->shared_from_this())); // A 

    // my_class will remain alive for this handler because it is bound 
    // via a shared_ptr. 
    io_service.post(boost::bind(&MyClass::action, 
           my_class->shared_from_this())); // B 

    // my_class will not be alive for this handler, because B will have 
    // been destroyed, and the my_class is reset before invoking the 
    // io_service. 
    io_service.post(weak_bind(&MyClass::action, 
          my_class->shared_from_this())); // C 

    // Reset the shared_ptr, resulting in the only remaining shared_ptr 
    // instance for my_class residing within handler B. 
    my_class.reset(); 
    io_service.run(); 
} 

並且得到的輸出:

MyClass() 
weak_binder::operator() 
MyClass::action() 
MyClass::action() 
~MyClass() 
weak_binder::operator()

如可以觀察到,MyClass::action()僅調用兩次:一次通過weak_binder而實例處於活動狀態(處理程序A),並且一旦通過boost::bind,實例通過shared_ptr(處理程序B)進行維護。處理程序C被調用,但weak_binder::operator()檢測到實例已被銷燬,導致無聲操作。