如何避免射击已经破坏了boost :: asio :: deadline_timer

时间:2017-03-27 11:29:18

标签: c++ boost timer asio

我在一个io_service对象上使用多个boost::asio::deadline_timerstd::shared_ptr的{​​{1}}存储在带有索引的容器boost::asio::deadline_timer中。

在计时器处理程序中,我删除了其他std::map<int, std::shared_ptr<debug_tim>> timers。但是,似乎擦除的计时器经常被成功错误代码触发。

有没有办法避免这种情况。我希望与删除的boost::asio::deadline_timer对应的计时器处理程序始终使用boost::asio::deadline_timer触发。

我错过了什么吗?

以下是重现行为的代码

https://wandbox.org/permlink/G0qzYcqauxdqw4i7

Operation canceled

在擦除计时器之前,我也会调用#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(); } 。但是,我得到了类似的结果。这是取消版本:

https://wandbox.org/permlink/uM0yMFufkyn9ipdG

boost::asio::deadline_timer::cancel()

修改

菲利克斯,谢谢你的回答。我理解#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(); } 的已删除对象的好方法吗?

修改

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

请参阅 How to avoid firing already destroyed boost::asio::deadline_timer

1 个答案:

答案 0 :(得分:1)

根据reference of deadline_timer::cancel

  

如果在调用cancel()时定时器已经过期,那么异步等待操作的处理程序将:

     
      
  • 已被调用;或

  •   
  • 已在不久的将来排队等待调用。

  •   
     

这些处理程序无法再被取消,因此会传递一个错误代码,表示等待操作已成功完成。

我们可以知道调用cancel()无法取消已经排队等待触发的计时器。

似乎dealine_timer不会覆盖析构函数。 (deadline_timer的成员列表中没有析构函数)

在您的代码段中,所有计时器几乎会在同一时间触发。关于asio将使用一些内部线程,很可能在调用一个完成处理程序时,其他人正在排队。