Asio15-QuitGracefully
Asio15-QuitGracefully.md
服务器优雅退出一直是服务器设计必须考虑的一个方向,意在能通过捕获信号使服务器安全退出。我们可以通过asio提供的信号机制绑定回调函数即可实现优雅退出。在主函数中我们添加
int main()
{
try {
boost::asio::io_context io_context;
boost::asio::signal_set signals(io_context, SIGINT, SIGTERM);
signals.async_wait([&io_context](const boost::system::error_code*error, int signal_number) {
io_context.stop();
});
CServer s(io_context, 10086);
io_context.run();
}
catch (std::exception& e) {
std::cerr << "Exception: " << e.what() << endl;
}
}
利用signal_set 定义了一系列信号合集,并且绑定了一个匿名函数,匿名函数捕获了io_context的引用,并且函数中设置了停止操作,也就是说当捕获到SIGINT,SIGTERM等信号时,会调用io_context.stop。
也可以使用c风格的信号:
bool stop = false;
std::mutex mtx;
std::condition_variable cv;
void sig_handle(int sig)
{
if (sig == SIGINT || sig == SIGTERM) {
std::unique_lock<std::mutex> lck(mtx);
stop = true;
cv.notify_one();
}
}
int main()
{
try {
boost::asio::io_context io_context;
std::thread net_work_thread([&io_context]() {
CServer s(io_context, 9999);
io_context.run();
});
std::signal(SIGINT, sig_handle);
std::signal(SIGTERM, sig_handle);
std::cout << "sad" << std::endl;
/**
* std::unique_lock<std::mutex> lk(mtx);
* while(!stop){
* cv.wait(lk); // 等信号
* }
*/
{
std::unique_lock<std::mutex> lk(mtx);
cv.wait(lk, [] { return stop; }); // 等信号
}
io_context.stop();
net_work_thread.join();
} catch (std::exception& e) {
std::cerr << "Exception: " << e.what() << endl;
}
}
区别在于,我们需要多开一个线程,用于支持服务的运行,主线程用于检测退出信号.
{
std::unique_lock<std::mutex> lk(mtx);
cv.wait(lk, [] { return stop; }); // 等信号
}
std::unique_lock<std::mutex> lk(mtx);
while(!stop){
cv.wait(lk); // 等信号
}
我们看到,这里对于等待条件变量两种写法,第一种是现代cpp支持的写法,cv.wait的第二个参数传入谓词,通过检测stop的值可以防止虚假唤醒.
第二种则是传统的使用方法,使用循环避免虚假唤醒.实际上第一种等价于第二种.
对于第二种,可能会写成如下形式:
while(!stop){
std::unique_lock<std::mutex> lk(mtx);
cv.wait(lk); // 等信号
}
这样在判断stop的时候没有加锁,那么与其他线程同时修改判断就会导致数据竞争,触发未定义.
如下情况,可能漏掉notify:
主线程:while (!stop) // 判断为 true,准备进入循环体 信号处理器:stop = true; cv.notify_one(); // 通知已发出 主线程:std::unique_lock<std::mutex> lk(mtx); cv.wait(lk); // 永远阻塞:通知已错过

浙公网安备 33010602011771号