取消deadline_timer,无论如何都会触发回调
cancel a deadline_timer, callback triggered anyway
我很惊讶没有在boost::asio(我们广泛使用的库(中找到时钟组件,所以它尝试制作一个简单、简约的实现来测试我的一些代码。
使用boost::asio::deadline_timer
,我制作了以下类别的
class Clock
{
public:
using callback_t = std::function<void(int, Clock&)>;
using duration_t = boost::posix_time::time_duration;
public:
Clock(boost::asio::io_service& io,
callback_t callback = nullptr,
duration_t duration = boost::posix_time::seconds(1),
bool enable = true)
: m_timer(io)
, m_duration(duration)
, m_callback(callback)
, m_enabled(false)
, m_count(0ul)
{
if (enable) start();
}
void start()
{
if (!m_enabled)
{
m_enabled = true;
m_timer.expires_from_now(m_duration);
m_timer.async_wait(boost::bind(&Clock::tick, this, _1)); // std::bind _1 issue ?
}
}
void stop()
{
if (m_enabled)
{
m_enabled = false;
size_t c_cnt = m_timer.cancel();
#ifdef DEBUG
printf("[DEBUG@%p] timer::stop : %lu ops cancelledn", this, c_cnt);
#endif
}
}
void tick(const boost::system::error_code& ec)
{
if(!ec)
{
m_timer.expires_at(m_timer.expires_at() + m_duration);
m_timer.async_wait(boost::bind(&Clock::tick, this, _1)); // std::bind _1 issue ?
if (m_callback) m_callback(++m_count, *this);
}
}
void reset_count() { m_count = 0ul; }
size_t get_count() const { return m_count; }
void set_duration(duration_t duration) { m_duration = duration; }
const duration_t& get_duration() const { return m_duration; }
void set_callback(callback_t callback) { m_callback = callback; }
const callback_t& get_callback() const { return m_callback; }
private:
boost::asio::deadline_timer m_timer;
duration_t m_duration;
callback_t m_callback;
bool m_enabled;
size_t m_count;
};
然而,stop
方法似乎不起作用。如果我要求Clock c2
停止另一个Clock c1
boost::asio::io_service ios;
Clock c1(ios, [&](int i, Clock& self){
printf("[C1 - fast] tick %dn", i);
}, boost::posix_time::millisec(100)
);
Clock c2(ios, [&](int i, Clock& self){
printf("[C2 - slow] tick %dn", i);
if (i%2==0) c1.start(); else c1.stop(); // Stop and start
}, boost::posix_time::millisec(1000)
);
ios.run();
我看到两个时钟都像预期的那样滴答作响,有时c1一秒钟都不停,而它应该停。
由于某些同步问题,调用m_timer.cancel()
似乎并不总是有效。我做错什么了吗?
首先,让我们展示重现的问题:
Coliru直播 (代码如下(
正如你所看到的,我把它作为运行
./a.out | grep -C5 false
当真的
c1_active
为假(并且完成处理程序不应该运行(时,这会过滤从C1的完成处理程序打印的记录的输出
简而言之,问题是一个"合乎逻辑"的种族条件。
这有点令人费解,因为只有一根线(表面可见(。但实际上并不太复杂。
结果是:
-
当Clock C1到期时,它会将其完成处理程序发布到
io_service
的任务队列中。这意味着它可能不会立即运行。 -
假设C2也过期了,它的完成处理程序现在被调度并在C1刚刚推送的处理程序之前执行。想象一下,这一次非常巧合的是,C2决定在C1上调用
stop()
。 -
C2的完成处理程序返回后,C1的完成处理函数被调用。
OOPS
它仍然有
ec
说"没有错误"。。。因此,C1的最后期限计时器被重新安排。哎呀。
背景
有关Asio(不会(为执行完成处理程序的顺序提供保证的更深入的背景信息,请参阅
- 取消的boost::asio处理程序的处理程序何时运行
解决方案
最简单的解决方案是实现m_enabled
可以是false
。让我们添加检查:
void tick(const boost::system::error_code &ec) {
if (!ec && m_enabled) {
m_timer.expires_at(m_timer.expires_at() + m_duration);
m_timer.async_wait(boost::bind(&Clock::tick, this, _1));
if (m_callback)
m_callback(++m_count, *this);
}
}
在我的系统上,它不再重现问题:(
再现器
在Coliru上直播
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/date_time/posix_time/posix_time_io.hpp>
static boost::posix_time::time_duration elapsed() {
using namespace boost::posix_time;
static ptime const t0 = microsec_clock::local_time();
return (microsec_clock::local_time() - t0);
}
class Clock {
public:
using callback_t = std::function<void(int, Clock &)>;
using duration_t = boost::posix_time::time_duration;
public:
Clock(boost::asio::io_service &io, callback_t callback = nullptr,
duration_t duration = boost::posix_time::seconds(1), bool enable = true)
: m_timer(io), m_duration(duration), m_callback(callback), m_enabled(false), m_count(0ul)
{
if (enable)
start();
}
void start() {
if (!m_enabled) {
m_enabled = true;
m_timer.expires_from_now(m_duration);
m_timer.async_wait(boost::bind(&Clock::tick, this, _1)); // std::bind _1 issue ?
}
}
void stop() {
if (m_enabled) {
m_enabled = false;
size_t c_cnt = m_timer.cancel();
#ifdef DEBUG
printf("[DEBUG@%p] timer::stop : %lu ops cancelledn", this, c_cnt);
#endif
}
}
void tick(const boost::system::error_code &ec) {
if (ec != boost::asio::error::operation_aborted) {
m_timer.expires_at(m_timer.expires_at() + m_duration);
m_timer.async_wait(boost::bind(&Clock::tick, this, _1));
if (m_callback)
m_callback(++m_count, *this);
}
}
void reset_count() { m_count = 0ul; }
size_t get_count() const { return m_count; }
void set_duration(duration_t duration) { m_duration = duration; }
const duration_t &get_duration() const { return m_duration; }
void set_callback(callback_t callback) { m_callback = callback; }
const callback_t &get_callback() const { return m_callback; }
private:
boost::asio::deadline_timer m_timer;
duration_t m_duration;
callback_t m_callback;
bool m_enabled;
size_t m_count;
};
#include <iostream>
int main() {
boost::asio::io_service ios;
bool c1_active = true;
Clock c1(ios, [&](int i, Clock& self)
{
std::cout << elapsed() << "t[C1 - fast] tick" << i << " (c1 active? " << std::boolalpha << c1_active << ")n";
},
boost::posix_time::millisec(1)
);
#if 1
Clock c2(ios, [&](int i, Clock& self)
{
std::cout << elapsed() << "t[C2 - slow] tick" << i << "n";
c1_active = (i % 2 == 0);
if (c1_active)
c1.start();
else
c1.stop();
},
boost::posix_time::millisec(10)
);
#endif
ios.run();
}
来自boost文档:
如果调用cancel((时计时器已经过期,那么异步等待操作的处理程序将:
- 已被调用
- 或者已经排队等待在不久的将来调用
这些处理程序不能再被取消,因此被传递了一个错误代码,指示成功完成等待操作。
你的应用程序在成功完成时(当计时器已经过期时(再次重新启动计时器,另一件有趣的事情是,在Start函数上再次调用你,以防计时器尚未过期时隐式取消计时器。
expires_at函数设置过期时间。任何挂起的异步等待操作将被取消。每个取消的处理程序将使用调用操作boost::asio::error::operation_abored错误代码。
也许您可以重用m_enabled变量,或者只使用另一个标志来检测计时器取消。
另一种解决方案是可能的:计时器示例
- 架构决策:返回std::future还是提供回调
- 正在为Xtensa simcall函数编写回调函数
- 如何在C++中使用非静态成员函数作为回调函数
- FLTK:按下哪个按钮 - 将数字传递给按钮的回调 (lambda)
- 在简单示例中,Python3 + ctypes 回调会导致内存泄漏
- 用于在回调中调用解析器的设计模式
- 如何使用C++对象的成员函数作为 C 样式回调?
- Java从C++回调到C++回调
- 如何将成员函数作为回调参数传递给需要"typedef-ed"自由函数指针的函数?
- 从不同的 cpp 调用回调函数会导致bad_function_call
- pcap_handler回调仅在使用 NPCAP v0.9991 时包含空数据包
- 不带轮询的 SDL2 事件回调
- C++存储带有可变参数的回调
- 如何使用 Node-addon-API 实现 node-nan 回调
- 处理影响跨不同线程共享对象的定时回调的最佳方法是什么?
- 访问类C++ C 样式回调
- 处理类内的回调时,必须调用对非静态成员函数的引用
- 如果 C 函数仍然可以间接执行(通过回调函数),那么将它声明为静态函数是否是一种不好的做法?
- 在C++中实现回调
- 取消deadline_timer,无论如何都会触发回调