我在一个io_service对象上使用多个boost::asio::deadline_timer
。 std::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
答案 0 :(得分:1)
根据reference of deadline_timer::cancel:
如果在调用cancel()时定时器已经过期,那么异步等待操作的处理程序将:
已被调用;或
已在不久的将来排队等待调用。
这些处理程序无法再被取消,因此会传递一个错误代码,表示等待操作已成功完成。
我们可以知道调用cancel()
无法取消已经排队等待触发的计时器。
似乎dealine_timer不会覆盖析构函数。 (deadline_timer的成员列表中没有析构函数)
在您的代码段中,所有计时器几乎会在同一时间触发。关于asio将使用一些内部线程,很可能在调用一个完成处理程序时,其他人正在排队。