[英]How to cleanly exit a threaded C++ program?
I am creating multiple threads in my program. 我正在我的程序中创建多个线程。 On pressing Ctrl-C , a signal handler is called.
按Ctrl-C时 ,将调用信号处理程序。 Inside a signal handler, I have put
exit(0)
at last. 在信号处理程序内部,我已经将
exit(0)
放在最后。 The thing is that sometimes the program terminates safely but the other times, I get runtime error stating 问题是,有时程序安全终止,但有时,我得到运行时错误说明
abort() has been called
So what would be the possible solution to avoid the error? 那么避免错误的可能解决方案是什么?
The usual way is to set an atomic flag (like std::atomic<bool>
) which is checked by all threads (including the main thread). 通常的方法是设置一个原子标志(如
std::atomic<bool>
),由所有线程(包括主线程)检查。 If set, then the sub-threads exit, and the main thread starts to join
the sub-threads. 如果设置,则子线程退出,主线程开始
join
子线程。 Then you can exit cleanly. 然后你可以干净利落地退出。
If you use std::thread
for the threads, that's a possible reason for the crashes you have. 如果您对线程使用
std::thread
,那么这可能是您崩溃的原因。 You must join
the thread before the std::thread
object is destructed. 您必须在
std::thread
对象被破坏之前join
该线程。
Others have mentioned having the signal-handler set a std::atomic<bool>
and having all the other threads periodically check that value to know when to exit. 其他人提到让信号处理程序设置为
std::atomic<bool>
并让所有其他线程定期检查该值以知道何时退出。
That approach works well as long as all of your other threads are periodically waking up anyway, at a reasonable frequency. 只要所有其他线程以合理的频率周期性地唤醒,那么这种方法效果很好。
It's not entirely satisfactory if one or more of your threads is purely event-driven, however -- in an event-driven program, threads are only supposed to wake up when there is some work for them to do, which means that they might well be asleep for days or weeks at a time. 如果你的一个或多个线程纯粹是事件驱动的,那就不完全令人满意了 - 但是在一个事件驱动的程序中,线程只有在它们有一些工作要做时才会被唤醒,这意味着它们可能很好一次睡几天或几周。 If they are forced to wake up every (so many) milliseconds simply to poll an atomic-boolean-flag, that makes an otherwise extremely CPU-efficient program much less CPU-efficient, since now every thread is waking up at short regular intervals, 24/7/365.
如果他们被迫每隔(这么多)毫秒被唤醒只是为了轮询一个原子布尔标志,这使得一个非常高CPU效率的程序的CPU效率更低,因为现在每个线程都以很短的规则间隔唤醒, 24/7/365。 This can be particularly problematic if you are trying to conserve battery life, as it can prevent the CPU from going into power-saving mode.
如果您试图节省电池寿命,这可能会特别成问题,因为它可能会阻止CPU进入省电模式。
An alternative approach that avoids polling would be this one: 避免轮询的另一种方法是:
select()
fd_set (or take a similar action for poll()
or whatever wait-for-IO function that thread blocks in) select()
fd_set中包含接收套接字(或对poll()
或线程阻塞的任何等待IO函数采取类似的操作) select()
call to immediately return, with FD_ISSET(receivingSocket)
indicating true because of the received byte select()
调用立即返回,因为收到的字节, FD_ISSET(receivingSocket)
指示为true join()
on each child thread, so that it can be guaranteed that all of the child threads are actually gone before main() returns. join()
,这样可以保证所有子线程在 main()返回之前实际上已经消失。 (This is necessary because otherwise there is a risk of a race condition -- eg the post-main() cleanup code might occasionally free a resource while a still-executing child thread was still using it, leading to a crash) The first thing you must accept is that threading is hard. 你必须接受的第一件事是线程很难。
A "program using threading" is about as generic as a "program using memory", and your question is similar to "how do I not corrupt memory in a program using memory?" “使用线程的程序”与“使用内存的程序”一样通用,你的问题类似于“如何使用内存破坏程序中的内存?”
The way you handle threading problem is to restrict how you use threads and the behavior of the threads. 处理线程问题的方法是限制线程的使用方式和线程的行为。
If your threading system is a bunch of small operations composed into a data flow network, with an implicit guarantee that if an operation is too big it is broken down into smaller operations and/or does checkpoints with the system, then shutting down looks very different than if you have a thread that loads an external DLL that then runs it for somewhere from 1 second to 10 hours to infinite length. 如果您的线程系统是由数据流网络组成的一堆小操作,则隐含保证如果操作太大,则会将其分解为较小的操作和/或与系统进行检查点,然后关闭看起来非常不同比如果你有一个线程加载一个外部DLL然后运行它从1秒到10小时到无限长度的某个地方。
Like most things in C++, solving your problem is going to be about ownership, control and (at a last resort) hacks. 像C ++中的大多数东西一样,解决你的问题将是关于所有权,控制和(最后的手段)黑客攻击。
Like data in C++, every thread should be owned. 与C ++中的数据一样,每个线程都应该被拥有。 The owner of a thread should have significant control over that thread, and be able to tell it that the application is shutting down.
线程的所有者应该对该线程有很大的控制权,并且能够告诉它应用程序正在关闭。 The shut down mechanism should be robust and tested, and ideally connected to other mechanisms (like early-abort of speculative tasks).
关闭机制应该是健壮的并且经过测试,并且理想地连接到其他机制(例如早期中止投机任务)。
The fact you are calling exit(0) is a bad sign. 你调用exit(0)的事实是一个坏兆头。 It implies your main thread of execution doesn't have a clean shutdown path.
它意味着您的主要执行线程没有干净的关闭路径。 Start there;
从那里开始; the interrupt handler should signal the main thread that shutdown should begin, and then your main thread should shut down gracefully.
中断处理程序应该通知主线程应该开始关闭,然后你的主线程应该正常关闭。 All stack frames should unwind, data should be cleaned up, etc.
所有堆栈帧都应该展开,数据应该清理,等等。
Then the same kind of logic that permits that clean and fast shutdown should also be applied to your threaded off code. 然后,同样类型的逻辑允许干净和快速关闭也应该应用于您的线程代码。
Anyone telling you it is as simple as a condition variable/atomic boolean and polling is selling you a bill of goods. 有人告诉你它就像条件变量/原子布尔一样简单,并且轮询正在向你出售货物清单。 That will only work in simple cases if you are lucky, and determining if it works reliably is going to be quite hard.
如果你很幸运,那只会在简单的情况下起作用,并且确定它是否可靠地工作会非常困难。
Additional to Some programmer dude answer and related to discussion in the comment section, you need to make the flag that controls termination of your threads as atomic
type. 除了一些程序员老兄的回答以及与评论部分中的讨论相关的内容之外,您还需要将控制线程终止的标志作为
atomic
类型。
Consider following case : 考虑以下案例:
bool done = false;
void pending_thread()
{
while(!done)
{
std::this_thread::sleep(std::milliseconds(1));
}
// do something that depends on working thread results
}
void worker_thread()
{
//do something for pending thread
done = true;
}
Here worker thread can be your main
thread also and done
is terminating flag of your thread, but pending thread need to do something with given data by working thread, before exiting. 这里工作线程也可以是你的
main
线程,并且done
了你的线程的终止标志,但是挂起的线程需要在退出之前通过工作线程对给定数据执行某些操作。
this example has race condition and undefined behaviour along with it, and it's really hard to find what is the actual problem int the real world. 这个例子有竞争条件和未定义的行为,并且很难找到真实世界中的实际问题。
Now the corrected version using std::automic
: 现在使用
std::automic
的更正版本:
std::atomic<bool> done(false);
void pending_thread()
{
while(!done.load())
{
std::this_thread::sleep(std::milliseconds(1));
}
// do something that depends on working thread results
}
void worker_thread()
{
//do something for pending thread
done = true;
}
You can exit thread without being concern of race condition or UB. 您可以在不考虑竞争条件或UB的情况下退出线程。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.