2.1 线程的基本管控
发起线程
void f(){
std::cout << "hello, thread\n";
}
std::thread thread1(f); // 普通函数作为thread构造函数的参数
与C++标准库中的许多类型相同,任何可调用类型(called type)
都适用于std::thread
。
仿函数作为参数
class background_task {
public:
void operator()()
{
std::cout << "background_task\n";
}
};
std::thread thread1( background_task() ); // 会把这句话当成函数声明
thread1.join();
std::thread thread1{ background_task() }; // 使用c++11的列表初始化
lambda表达式作为参数
std::string hellostr = "hello";
std::thread thread1([](std::string str){
std::cout << str << '\n';
}, hellostr);
线程等待
一旦启动了线程,就需要明确是要等待它结束(与之汇合),还是任由它独自运行(与之分离)。如果等到
std::thread
对象销毁时还没决定好,std::thread
的析构函数将调用std::terminate
终止整个程序。注意,我们只需在std::thread
对象销毁前做出决定即可,线程本身可能在汇合或分离前就早已结束。
若需等待线程完成,只需在与之关联的std::thread
实例上,通过调用成员函数join()
实现。主线程会阻塞在join()
处直到等待的线程结束运行。只要调用了join()
,隶属于该线程的任何存储空间即会因此清除,std::thread
对象遂不再关联到已结束的线程上。事实上,它与任何线程无关联。
join()
只能调用一次,只要std::thread
对象曾经调用过join()
,线程就变得不可再汇合(joinable
),成员函数joinable()
将返回false
。
在出现异常的情况下等待
如果线程启动以后有异常抛出,而join()
尚未执行,则该join()
会被略过。
-
使用
try/catch
块try { do_something_current_thread(); } catch(...) { t.join(); throw; } t.join();
-
RAII手法
class thread_guard { std::thread &t; // thread不能拷贝,用引用管理,如果不加引用,构造的时候一定会进行拷贝构造 public: explicit thread_guard(std::thread &_t): t(_t) { } // 同理,参数也必须加引用避免拷贝 ~thread_guard() { if(t.joinable()) t.join(); } thread_guard(thread_guard const &) = delete; thread_guard operator=(thread_guard const &) = delete; }; void f() { std::thread t(myfunc); thread_guard g(t); do_something_in_current_thread(); }
即便主线程抛出异常,函数f退出,局部对象g会被销毁,其析构函数中会将新线程汇合。
线程detach
调用std::thread
对象的成员函数detach()
,会令线程在后台运行,遂无法与之直接通信。其归属权和控制权都转移给C++运行时库,一旦线程退出,与之关联的资源都会被正确回收。
调用t.detach()
后,std::thread
对象不再关联实际的执行线程,即变得不可汇合。
如果要把std::thread
对象和线程分离,就必须存在与其关联的执行线程。与join()
相同,即只有当t.joinable()
返回true
时,才能调用t.detach()
。