管理线程之等待线程结束
正常环境下等待线程结束
假设须要等待线程结束。就在线程的实例对象上调用join()。
在管理线程之创建线程最后的样例中,用my_thread.join()取代my_thread.detach()就能够确保在函数终止前、局部变量析构前。线程会终止。在这样的情况下,用分开的线程来执行函数就没有什么意义了。
由于在等待my_thread终止时,这个线程就不做不论什么事情了。在实际的project应用中,要么这个线程做自己的事。要么创建多个线程等待它们结束。
join()是简单粗暴的,或者你等待线程终止。或者你不等待。
假设想要更加精确控制,比如检查这个线程是否终止,或不过等待某一段时间。这是你必须使用其它方法比如条件变量等。线程调用join()会清理对应的内存空间。调用join()后,std::thread对象就不再和这个线程相关了。这也就是意味着。对于一个线程实例对象,你只能够调用join()一次,在调用后。这个线程对象就不在是joinable,调用joinable()会返回false。
异常环境下的情况
可是假设要等待一个线程,那么必须小心去找到在代码的什么地方调用join()。也就是说。由于异常的发生,join()有可能被跳过而得不到运行。
通常来讲,在没有异常情况下假设调用join(),那么再异常情况下也须要调用join(),这样才干够避免意外的生命周期的问题。
在实际代码中。能够使用try/catch:
struct func; void f() { int some_local_state=0; func my_func(some_local_state); std::thread t(my_func); try { do_something_in_current_thread(); } catch(...) { t.join(); throw; } t.join(); }上面代码使用try/catch模块来确保调用join()。不管是否发生异常。
使用try/catch有点冗长(verbose)。并且easy出现作用域错误,不是理想的解决方法。
class thread_guard { std::thread& t; public: explicit thread_guard(std::thread& t_):t(t_){} ~thread_guard() { if(t.joinable())//先检查是否已经掉用过join t.join(); } //以下是禁止使用拷贝构造函数和赋值操作符。这须要 //C++11的支持 thread_guard(thread_guard const&)=delete; thread_guard& operator=(thread_guard const&)=delete; }; struct func; void f() { int some_local_state=0; func my_func(some_local_state); std::thread t(myfunc); thread_guard g(t); do_something_in_current_thread(); }上面的原理是局部变量会依照构造的逆顺序销毁,由于这些变量都在栈上。thread_guard对象g首先销毁。在它的析构函数调用了join()。
这是为了防止thread_guard对象超出thread对象的生命周期。
在后台执行线程
还有一种极端情况是,它能够使用分离的线程(假设有一种机制能够检測某个线程是否完毕任务,或线程是用来即发即弃任务“fire and forget”)。
这个应用能够同一时候编辑多个文档。编辑窗体是独立的,它们的代码同样,可是处理的数据不同。一种处理的方法就是在打开一个新的文档时就创建新的线程,然后分离。
void edit_document(std::string const& filename) { open_document_and_display_gui(filename); while(!done_editing())//编辑是否完毕 { user_command cmd=get_user_input();//用户输入命令 if(cmd.type==open_new_document)//要打开新文档 { std::string const new_name=get_filename_from_user(); std::thread t(edit_document,new_name);//创建线程 t.detach();//分离,在后台执行 } else { process_user_input(cmd); } } }