gpt4 book ai didi

c++ - 取消 deadline_timer,无论如何都会触发回调

转载 作者:塔克拉玛干 更新时间:2023-11-03 01:52:30 27 4
gpt4 key购买 nike

我很惊讶没有在 boost::asio(我们任何广泛使用的库)中找到时钟组件,所以它尝试制作一个简单、简约的实现来测试我的一些代码。

使用 boost::asio::deadline_timer 我制作了以下类(class)

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 cancelled\n", 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 %d\n", i);
}, boost::posix_time::millisec(100)
);
Clock c2(ios, [&](int i, Clock& self){
printf("[C2 - slow] tick %d\n", i);
if (i%2==0) c1.start(); else c1.stop(); // Stop and start
}, boost::posix_time::millisec(1000)
);
ios.run();

我看到两个时钟都按预期滴答作响,有时 c1 不会停止一秒钟,而它应该停止。

由于某些同步问题,调用 m_timer.cancel() 似乎并不总是有效。我是不是搞错了什么?

最佳答案

首先,让我们展示一下重现的问题:

Live On Coliru (代码如下)

As you can see I run it as

./a.out | grep -C5 false

This filters the output for records that print from C1's completion handler when really c1_active is false (and the completion handler wasn't expected to run)

简而言之,这个问题是一个“逻辑”竞争条件。

这有点费脑筋,因为只有一个线程(在表面上可见)。但实际上并不太复杂。

这是怎么回事:

  • 当时钟 C1 到期时,它会将其完成处理程序 发布到io_service 的任务队列中。这意味着它可能不会立即运行。

  • 假设 C2 也过期了,它的完成处理程序现在被安排并在 C1 刚刚推送的处理程序之前执行。想象一下,这一次 C2 决定调用 C1 上的 stop()

  • 在 C2 的完成处理程序返回后,将调用 C1 的完成处理程序。

    糟糕

    它仍然有 ec 说“没有错误”...因此 C1 的截止时间计时器被重新安排。糟糕。

背景

有关 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);
}
}

在我的系统上它不再重现问题:)

复制者

Live On 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 cancelled\n", 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();
}

关于c++ - 取消 deadline_timer,无论如何都会触发回调,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31192702/

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