2017-03-27 48 views
0

我在一個io_service對象上使用了多個boost::asio::deadline_timerstd::shared_ptrboost::asio::deadline_timer存儲在帶有索引的容器std::map<int, std::shared_ptr<debug_tim>> timers中。如何避免射擊已經破壞了boost :: asio :: deadline_timer

在定時器處理程序中,我擦除其他boost::asio::deadline_timer。但是,似乎擦除計時器通常會被成功錯誤代碼解僱。

有什麼辦法可以避免這種情況。我預計與刪除的boost::asio::deadline_timer相對應的定時器處理程序始終會以Operation canceled觸發。

我錯過了什麼嗎?

這裏是再現行爲

https://wandbox.org/permlink/G0qzYcqauxdqw4i7

#include <iostream> 
#include <memory> 

#include <boost/asio.hpp> 

// deadline_timer with index ctor/dtor print 
struct debug_tim : boost::asio::deadline_timer { 
    debug_tim(boost::asio::io_service& ios, int i) : boost::asio::deadline_timer(ios), i(i) { 
     std::cout << "debug_tim() " << i << std::endl; 
    } 
    ~debug_tim() { 
     std::cout << "~debug_tim() " << i << std::endl; 
    } 
    int i; 
}; 

int main() { 
    boost::asio::io_service ios; 
    std::map<int, std::shared_ptr<debug_tim>> timers; 
    { 
     for (int i = 0; i != 5; ++i) { 
      auto tim = std::make_shared<debug_tim>(ios, i); 
      std::cout << "set timer " << i << std::endl; 
      tim->expires_from_now(boost::posix_time::seconds(1)); 
      timers.emplace(i, tim); 
      tim->async_wait([&timers, i](auto ec){ 
        std::cout << "timer fired " << i << " : " << ec.message() << std::endl; 
        auto it = timers.find(i); 
        if (it == timers.end()) { 
         std::cout << " already destructed." << std::endl; 
        } 
        else { 
         int other_idx = i + 1; // erase other timer (e.g. i + 1) 
         timers.erase(other_idx); 
         std::cout << " erased " << other_idx << std::endl; 
        } 
       } 
      ); 
     } 
    } 
    ios.run(); 
} 

我也呼籲boost::asio::deadline_timer::cancel()之前,我抹去定時器的代碼。但是,我得到了類似的結果。這裏是取消版本:

https://wandbox.org/permlink/uM0yMFufkyn9ipdG

#include <iostream> 
#include <memory> 

#include <boost/asio.hpp> 

// deadline_timer with index ctor/dtor print 
struct debug_tim : boost::asio::deadline_timer { 
    debug_tim(boost::asio::io_service& ios, int i) : boost::asio::deadline_timer(ios), i(i) { 
     std::cout << "debug_tim() " << i << std::endl; 
    } 
    ~debug_tim() { 
     std::cout << "~debug_tim() " << i << std::endl; 
    } 
    int i; 
}; 

int main() { 
    boost::asio::io_service ios; 
    std::map<int, std::shared_ptr<debug_tim>> timers; 
    { 
     for (int i = 0; i != 5; ++i) { 
      auto tim = std::make_shared<debug_tim>(ios, i); 
      std::cout << "set timer " << i << std::endl; 
      tim->expires_from_now(boost::posix_time::seconds(1)); 
      timers.emplace(i, tim); 
      tim->async_wait([&timers, i](auto ec){ 
        std::cout << "timer fired " << i << " : " << ec.message() << std::endl; 
        auto it = timers.find(i); 
        if (it == timers.end()) { 
         std::cout << " already destructed." << std::endl; 
        } 
        else { 
         int other_idx = i + 1; // erase other timer (e.g. i + 1) 
         auto other_it = timers.find(other_idx); 
         if (other_it != timers.end()) { 
          other_it->second->cancel(); 
          timers.erase(other_it); 
         } 
         std::cout << " erased " << other_idx << std::endl; 
        } 
       } 
      ); 
     } 
    } 
    ios.run(); 
} 

編輯

菲利克斯,謝謝你的回答。我瞭解boost::asio::deadline::timer::cancel()的行爲。我總是需要關心boost::asio::deadline::timer的使用期限。我是我的項目的實際代碼,`boost :: asio :: deadline :: timer`是另一個對象(如會話對象)的成員變量。在定時器處理程序中,它訪問該對象。這很危險。

我考慮如何編寫安全代碼。我想出了使用std::weak_ptr爲了檢查對象的生命週期。

下面是更新後的代碼:

#include <iostream> 
#include <memory> 

#include <boost/asio.hpp> 

// deadline_timer with index ctor/dtor print 
struct debug_tim : boost::asio::deadline_timer { 
    debug_tim(boost::asio::io_service& ios, int i) : boost::asio::deadline_timer(ios), i(i) { 
     std::cout << "debug_tim() " << i << std::endl; 
    } 
    ~debug_tim() { 
     std::cout << "~debug_tim() " << i << std::endl; 
    } 
    int i; 
}; 

int main() { 
    boost::asio::io_service ios; 
    std::map<int, std::shared_ptr<debug_tim>> timers; 
    { 
     for (int i = 0; i != 5; ++i) { 
      auto tim = std::make_shared<debug_tim>(ios, i); 
      std::cout << "set timer " << i << std::endl; 
      tim->expires_from_now(boost::posix_time::seconds(1)); 
      timers.emplace(i, tim); 

      // Capture tim as the weak_ptr wp 
      tim->async_wait([&timers, i, wp = std::weak_ptr<debug_tim>(tim)](auto ec){ 
        std::cout << "timer fired " << i << " : " << ec.message() << std::endl; 

        // Check the lifetime of wp 
        if (!wp.lock()) std::cout << " timer freed." << std::endl; // return here on actual code 

        auto it = timers.find(i); 
        if (it == timers.end()) { 
         std::cout << " already destructed." << std::endl; 
        } 
        else { 
         int other_idx = i + 1; // erase other timer (e.g. i + 1) 
         timers.erase(other_idx); 
         std::cout << " erased " << other_idx << std::endl; 
        } 
       } 
      ); 
     } 
    } 
    ios.run(); 
} 

這是爲了避免訪問已刪除對象具有boost::asio::deadline_timer的好辦法?

編輯

我weak_ptr的解決方案效果很好。

How to avoid firing already destroyed boost::asio::deadline_timer

回答

1

按照reference of deadline_timer::cancel

如果計時器已過期時取消()被調用,然後異步等待操作處理程序將:

  • 已被調用;或

  • 已經排隊等候在不久的將來調用。

這些處理程序不能再被取消,因此會傳遞一個錯誤代碼,指示成功完成等待操作。

我們可以知道調用cancel()不能取消已經排隊等待發射的定時器。

而dealine_timer似乎並沒有重寫析構函數。 (deadline_timer的成員列表中沒有析構函數)

在您的代碼片段中,所有計時器幾乎在同一時間觸發。考慮到asio會使用一些內部線程,很可能當一個完成處理程序被調用時,其他人正在排隊。

+0

謝謝你的回答。我瞭解dead_time_timer的行爲。我想避免或想要知道取消狀態的原因是我訪問超時處理程序中具有deadline_timer的對象。 現在,我的目標已經改變爲如何避免訪問定時器處理程序中的已刪除資源。所以我更新(添加)我的問題。你能檢查我的方法嗎? –

+0

@ TakakoshiKondo是的,這很好。在同時發射時注意比賽條件。 – felix

+0

感謝您的評論! –

相關問題