我有一个boost::asio问题,当解构io_service时,发生了崩溃,代码中有什么问题?
backtrace info:
(gdb) bt
#0 boost::asio::detail::scheduler::work_finished (this=this@entry=0x0)
at /ephemeral/workspace/CB.SBTS_PSINT2.FLOW/build/build/sm6-snowfish-nrt/tmp/work/tremont-64-pc-linux-gnu/sysadapt/67c8d51ea9241fcd4ff1b192870be178f5a70540-r1/recipe-sysroot/usr/include/c++/10.2.0/bits/atomic_base.h:333
#1 0x0000000000785fe6 in boost::asio::io_context::work::~work (this=<synthetic pointer>, __in_chrg=<optimized out>)
at /ephemeral/workspace/CB.SBTS_PSINT2.FLOW/build/build/sm6-snowfish-nrt/tmp/work/tremont-64-pc-linux-gnu/sysadapt/67c8d51ea9241fcd4ff1b192870be178f5a70540-r1/recipe-sysroot/usr/include/boost/asio/impl/io_context.hpp:427
#2 common::IoServiceThreadGuard::IoServiceThreadGuard(boost::asio::io_context&, unsigned int)::{lambda()#1}::operator()() const (__closure=<optimized out>)
代码:
explicit IoServiceThreadGuard(boost::asio::io_service& ioService, unsigned int count) :
ioService_{ioService},
threadCount_(count)
{
for (unsigned int i = 0; i < threadCount_; ++i)
{
threads_.create_thread(
[&]()
{
boost::asio::io_service::work work(ioService_);
ioService_.run();
}); // NOLINT
}
}
~IoServiceThreadGuard()
{
try
{
if (not ioService_.stopped())
{
ioService_.stop();
}
threads_.join_all();
}
catch (const std::exception& e)
{
logger << ::info << "~IoServiceThreadGuard() throw error: " << e.what();
}
}
正如评论者所说,您需要处理异常-(是否应该捕获boost::asio::io_service::run()抛出的异常?)
然而,从查看代码来看,问题似乎更有可能是您通过引用获取了io_service
,并且在IoServiceThreadGuard
被销毁之前它可能超出了范围。
给它们相同的生命周期似乎更自然:
Live On Coliru
#include <boost/asio.hpp>
#include <boost/thread.hpp>
#include <iostream>
namespace asio = boost::asio;
using namespace std::chrono_literals;
static std::ostream logger(std::clog.rdbuf());
static constexpr char const* info = "INFO ";
class IoServiceThreadGuard {
public:
explicit IoServiceThreadGuard(unsigned count) : threadCount_(count) {
for (unsigned int i = 0; i < threadCount_; ++i) {
threads_.create_thread([&]() {
asio::io_service::work work(ioService_);
ioService_.run();
});
}
}
~IoServiceThreadGuard() {
try {
if (not ioService_.stopped()) {
ioService_.stop();
}
threads_.join_all();
} catch (std::exception const& e) {
logger << ::info << "~IoServiceThreadGuard() throw error: " << e.what();
}
}
asio::io_service& get_service() { return ioService_; }
private:
asio::io_service ioService_;
boost::thread_group threads_; // note that destruction is in reverse order of declaration
unsigned threadCount_;
};
int main() {
{
IoServiceThreadGuard io(10);
asio::steady_timer timer(io.get_service(), 1s);
timer.async_wait([](auto ec) { logger << ::info << "timer " << ec.message() << "n"; });
io.get_service().run_for(2s);
}
logger << "Done" << std::endl;
}
更好的是,使用未弃用的io_context
: Live
更好的是,放弃Boost Thread依赖并直接使用asio::thread_pool
。
现在这一切都工作在仅仅5行代码,它做正确的事情异常处理以及!
Live On Coliru
#include <boost/asio.hpp>
#include <boost/thread.hpp>
#include <iostream>
namespace asio = boost::asio;
using namespace std::chrono_literals;
static std::ostream logger(std::clog.rdbuf());
static constexpr char const* info = "INFO ";
struct IoThreads {
explicit IoThreads(unsigned count) : ioc_(count) {}
asio::thread_pool& get_context() { return ioc_; }
private:
asio::thread_pool ioc_;
};
int main() {
for (auto time_allotted : {0.5s, 2.0s}) {
logger << "Using 4 threads for " << time_allotted / 1.s << "s" << std::endl;
IoThreads io(4);
asio::steady_timer timer(io.get_context(), 1s);
timer.async_wait([](auto ec) { logger << ::info << "timer " << ec.message() << "n"; });
std::this_thread::sleep_for(time_allotted);
}
logger << "Done" << std::endl;
}
打印(总运行时间为2.518秒):
Using 4 threads for 0.5s
Using 4 threads for 2s
INFO timer Success
Done
简化、泛化、解耦
此时,请考虑忘记多余的"警卫"。类,并考虑传递一个执行器(按值),而不是硬编码asio::io_service&
、asio::io_context&
或asio::thread_pool&
。executor
是一个轻量级抽象,它将异步代码与执行上下文解耦。例如,您可以在多线程上下文中使用链执行器,而不需要异步代码知道。
Live On Coliru
#include <boost/asio.hpp>
#include <boost/thread.hpp>
#include <iostream>
namespace asio = boost::asio;
using boost::core::demangle;
using namespace std::chrono_literals;
class Demo { // just a sample, this could be a network server/client
public:
Demo(asio::any_io_executor ex) : timer_(ex) {} // take an executor, don't care what type
void run_async_stuff() {
std::cout << "Demo on " << demangle(timer_.get_executor().target_type().name()) << std::endl;
timer_.expires_after(1s);
timer_.async_wait(on_completion); // or something like `asio::async_read` etc.
}
private:
static void on_completion(boost::system::error_code ec) {
std::cout << "async_stuff " << ec.message() << "n";
}
asio::steady_timer timer_; // this could be tcp::socket or so
};
int main() {
{
asio::io_context io(1);
{
Demo demo(io.get_executor()); // no strand required
demo.run_async_stuff();
io.run_for(500ms);
} // ~Demo cancels uncompleted async operation
io.run(); // To see `operaion_aborted` completion
}
{
asio::thread_pool io(10);
Demo demo(make_strand(io)); // notice strand executor
demo.run_async_stuff();
std::this_thread::sleep_for(1.5s);
}
std::cout << "Done" << std::endl;
}
打印。
Demo on boost::asio::io_context::basic_executor_type<std::allocator<void>, 0ul>
async_stuff Operation canceled
Demo on boost::asio::strand<boost::asio::thread_pool::basic_executor_type<std::allocator<void>, 0u> >
async_stuff Success
Done