ef(arg)); // pass to thread by reference
std::thread t4(std::move(t3)); // t4 is now running func2(). t3 is no longer a thread
//t1.join() Error!
t2.join();
//t3.join() Error!
t4.join();
}
多数情况下我们使用的是上面第二种创建线程的方式。下面看一下join和detach。
4.2 join && detach
对于创建的线程,一般会在其销毁前调用join和detach函数;
弄清楚这两个函数的调用时机和意义,以及调用前后线程状态的变化非常重要。
- join 会使当前线程阻塞,直到目标线程执行完毕;
- 只有处于活动状态线程才能调用join,可以通过joinable()函数检查;
- joinable() == true表示当前线程是活动线程,才可以调用join函数;
- 默认构造函数创建的对象是joinable() == false;
- join只能被调用一次,之后joinable就会变为false,表示线程执行完毕;
- 调用 ternimate()的线程必须是 joinable() == false;
- 如果线程不调用join()函数,即使执行完毕也是一个活动线程,即joinable() == true,依然可以调用join()函数;
- detach 将thread对象及其表示的线程分离;
- 调用detach表示thread对象和其表示的线程完全分离;
- 分离之后的线程是不在受约束和管制,会单独执行,直到执行完毕释放资源,可以看做是一个daemon线程;
- 分离之后thread对象不再表示任何线程;
- 分离之后joinable() == false,即使还在执行;
join实例分析:
int main() {
thread t(tstart, "C++ 11 thread!");
cout << t.joinable() << endl;
if (t.joinable()) t.join();
//t.detach(); Error
cout << t.joinable() << endl;
// t.join(); Error
cout << "Main Function!" << endl;
system("pause");
}
简单来说就是只有处于活动状态的线程才可以调用join,调用返回表示线程执行完毕,joinable() == false.
inline void thread::join()
{ // join thread
if (!joinable())
_Throw_Cpp_error(_INVALID_ARGUMENT);
const bool _Is_null = _Thr_is_null(_Thr); // Avoid Clang -Wparentheses-equality
... ...
}
将上面的t.join()换成是t.detach()会得到相同的结果.
void detach()
{ // detach thread
if (!joinable())
_Throw_Cpp_error(_INVALID_ARGUMENT);
_Thrd_detachX(_Thr);
_Thr_set_null(_Thr);
}
上面是thread文件中对detach的定义,可以看出只有joinable() == true的线程,也就是活动状态的线程才可以调用detach。
~thread() _NOEXCEPT
{ // clean up
if (joinable())
_XSTD terminate();
}
当线程既没有调用join也没有调用detach的时候,线程执行完毕joinable() == true,那么当thread对象被销毁的时候,会调用terminate()。
4.3 获取线程ID
线程ID是一个线程的标识符,C++标准中提供两种方式获取线程ID;
- thread_obj.get_id();
- std::this_thread::get_id()
有一点需要注意,就是空thread对象,也就是不表示任何线程的thread obj调用get_id返回值为0;
此外当一个线程被detach或者joinable() == false时,调用get_id的返回结果也为0。
cout << t.get_id() << ' ' << this_thread::get_id() << endl;
//t.detach();
t.join();
cout << t.get_id() << ' ' << std::this_thread::get_id() << endl;
4.4 交换thread表示的线程
除了上面介绍的detach可以分离thread对象及其所表示的线程,或者move到别的线程之外,还可以使用swap来交换两个thread对象表示的线程。
实例来看一下两个线程的交换。
int tstart(const string& tname) {
cout << "Thread test! " << tname << endl;
return 0;
}
int main() {
thread t1(tstart, "C++ 11 thread_1!");
thread t2(tstart, "C++ 11 thread_2!");
cout << "current thread id: " << this_thread::get_id() << endl;
cout << "before swap: "<< " thread_1 id: " << t1.get_id() << " thread_2 id: " << t2.get_id() << endl;
t1.swap(t2);
cout << "after swap: " << " thread_1 id: " << t1.get_id() << " thread_2 id: " << t2.get_id() << endl;
//t.detach();
t1.join();
t2.join();
}
结果:
Thread test! C++ 11 thread_1!
Thread test! C++ 11 thread_2!
current thread id: 39308
before swap: thread_1 id: 26240 thread_2 id: 37276
after swap: thread_1 id: 37276 thre