C++多线程编程:其一、join()和joinable()函数

一、join的功能

假设有两个线程,线程A和线程B。线程A被托管在thread对象A中。在线程B中执行对象A的join()函数,那么线程B就会被阻塞住,直到线程A执行完成后,线程B才会执行A.join()后面的代码。
看代码:

void f(){
    Sleep(1000);
    cout<<"I am f thread"<<endl;
}
int main()
{
    thread t1(f);
    t1.join();
    cout<<"run main thread"<<endl;
}

执行结果:

I am f thread
run main thread

但是如果创建一个空的thread对象

int main()
{
    thread t1;
    t1.join();
    cout<<"run main thread"<<endl;
}

运行结果:

terminate called after throwing an instance of 'std::system_error'
  what():  Invalid argument

进程已结束,退出代码3

上一篇博客中提到了,空的thread对象,其实是没有持有操作系统相关的资源,这种情况是不能join的。

二、joinable()函数

joinable()函数的功能,官方文档说的非常晦涩难懂。我个人比较倾向于这种解释:“判断当前的线程对象是否是可执行线程对象。”
一般有三种情况,joinable()返回false:
(1)该thread对象通过无参构造函数构造;
(2)该thread对象执行过join()或者detach();
(3)该thread对象被move过,但是move的接受方的joinable()结果为true。
看代码:

void f(){
}
int main()
{
    thread t1;
    thread t2(f);
    t1=move(t2);
    cout<<"t1 : "<<t1.joinable()<<endl;
    cout<<"t2 : "<<t2.joinable()<<endl;
    t1.join();
}

输出结果:

t1 : 1
t2 : 0

三、join函数到底干了什么?

一个非常容易忽略的点:join函数实际上是放弃了持有的线程资源。对于没有持有线程资源的thread对象不能join,这是为了保证thread对象只能join一次。joinable函数实际上是检测thread对象是否持有线程资源。
假如说我们创建了一个空的thread对象,没有传入函数,自然不会持有操作系统层面的线程资源。强行join就会报错。
假如我们给它move进来一个资源以后,那么它就可以join了。
这个对象在执行了join函数以后,彻底放弃了对于线程资源的管理权,joinable状态变成了false。
同样的道理,thread被move以后,放弃了线程资源,joinable状态变成了false。
一句话:joinable()返回值为true的对象,也就是持有线程资源的对象,才能join。

四、必须join或者detach吗?

从宏观上看,是的。
一句话描述:C++程序中,在thread对象执行析构函数的时候,不允许持有线程资源。执行析构之前,要么通过join放弃资源的控制权,要么将线程资源move出去。

~thread() _NOEXCEPT
{	// clean up
	if (joinable())
		_XSTD terminate();
}

从源码上看,直接以异常形式退出程序。

五、线程资源不能被覆盖

看代码:

void f(){
}
int main()
{
    thread t1(f);
    thread t2(f);
    t1=move(t2);   //覆盖t1持有的线程资源
    cout<<"t1 : "<<t1.joinable()<<endl;
    cout<<"t2 : "<<t2.joinable()<<endl;
    t1.join();
}

执行结果:

terminate called without an active exception

进程已结束,退出代码3

你可能感兴趣的:(c++,开发语言)