gpt4 book ai didi

c++ - 如何避免触发已经销毁的 boost::asio::deadline_timer

转载 作者:行者123 更新时间:2023-11-30 05:15:32 30 4
gpt4 key购买 nike

我正在使用多个 boost::asio::deadline_timer在一个 io_service 对象上。 std::shared_ptrboost::asio::deadline_timer存储在容器中 std::map<int, std::shared_ptr<debug_tim>> timers带索引。

在计时器处理程序中,我删除其他 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` 是另一个对象(例如 session 对象)的成员变量。在计时器处理程序中,它访问该对象。这很危险。

我考虑如何编写安全的代码。我想出了使用 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

最佳答案

根据reference of deadline_timer::cancel :

If the timer has already expired when cancel() is called, then the handlers for asynchronous wait operations will:

  • have already been invoked; or

  • have been queued for invocation in the near future.

These handlers can no longer be cancelled, and therefore are passed an error code that indicates the successful completion of the wait operation.

我们可以知道调用cancel()并不能取消已经排队等待触发的定时器。

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

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

关于c++ - 如何避免触发已经销毁的 boost::asio::deadline_timer,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43045192/

30 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com