问题描述
我有一些由asio::steady_timer
管理的封装逻辑。在“对象寿命真正结束”之后,无需调用此逻辑。这意味着不需要将对象寿命延长到最后一次调用计时器的完成处理程序。我可以取消对象的析构函数中的计时器,而无需等待何时调用完成处理程序吗?
#include <memory>
#include <thread>
#include <boost/asio.hpp>
#include <boost/asio/steady_timer.hpp>
namespace asio = boost::asio;
class Foo : public std::enable_shared_from_this<Foo>
{
public:
explicit Foo(asio::io_context& ioc)
: m_timer(ioc)
{}
~Foo()
{
std::lock_guard<std::mutex> lock(m_mutex);
m_timer.cancel();
}
void update()
{
std::lock_guard<std::mutex> lock(m_mutex);
m_timer.expires_after(std::chrono::seconds(1));
m_timer.async_wait(
[=](const boost::system::error_code& ec)
{
if (!ec) {
update();
}
}
);
// ...
}
private:
mutable std::mutex m_mutex;
asio::steady_timer m_timer;
};
int main()
{
asio::io_context ioContext;
auto foo = std::make_shared<Foo>(ioContext);
foo->update();
std::thread thread(
[&]
{
ioContext.run();
}
);
foo.reset();
thread.join();
return 0;
}
更新:
根据destructor,实际上不需要强制调用cancel()
。因此(在上述情况下)问题是:在所有完成处理程序都被调用之前,我是否必须保持封装的steady_timer
处于活动状态?
解决方法
根据我对deadline_timer
的经验(但我会说它与steady timer
的机制相同)-是的,您必须保持计时器对象处于活动状态,直到调用所有完成处理程序。在销毁计时器的情况下取消计时器并没有帮助,因为无法保证在销毁计时器之前将其取消。由于可以在销毁计时器对象的同时调用完成处理程序,因此可能会发生段错误。我遇到的情况是,与在Linux上相比,它在Windows上通常更可重现,因此请谨慎测试。
对不起,我上面的错误代码。语句“这意味着不需要延长对象寿命来最后调用计时器的完成处理程序。” 是错误的。始终应延长对象寿命,直到调用所有完成处理程序为止。
我添加了公共成员函数Foo::cancel
,以便能够从外部取消预定的操作。
#include <memory>
#include <thread>
#include <boost/asio.hpp>
#include <boost/asio/steady_timer.hpp>
namespace asio = boost::asio;
class Foo : public std::enable_shared_from_this<Foo>
{
public:
explicit Foo(asio::io_context& ioc)
: m_timer(ioc)
{}
void update()
{
std::lock_guard<std::mutex> lock(m_mutex);
auto self(shared_from_this());
m_timer.expires_after(std::chrono::seconds(1));
m_timer.async_wait(
[this,self](const boost::system::error_code& ec)
{
if (!ec) {
update();
}
}
);
// ...
}
void cancel()
{
std::lock_guard<std::mutex> lock(m_mutex);
m_timer.cancel();
}
private:
mutable std::mutex m_mutex;
asio::steady_timer m_timer;
};
int main()
{
asio::io_context ioContext;
auto foo = std::make_shared<Foo>(ioContext);
foo->update();
std::thread thread(
[&]
{
ioContext.run();
}
);
foo->cancel();
foo.reset();
thread.join();
return 0;
}