thread类
thread
头文件:thread
名称空间:std
thread
类用于创建线程对象。
构造函数
1、默认构造函数
thread() noexcept;//默认构造函数
默认构造函数会构造一个thread
对象,但该对象不表示任何可执行的线程,并且不是joinable
。
#include <iostream>
#include <thread>
int main(int argc, char*argv[]){
//使用默认构造函数创建对象,non-joinable状态
std::thread th1;
}
2、带形参的构造函数
//带形参的构造函数
template <class Fn, class... Args>
explicit thread (Fn&& fn, Args&&... args);
该构造函数会构造一个thread
对象,表示新的可执行的joinable
线程。新线程会调用fn
,而args
用于初始化fn
的形参。
fn
可以是函数指针、对象成员函数指针,或任何的移动构造函数对象(如定义了opearotr()
的类对象),返回值被忽略。
该构造的完成与该fn副本的调用开始同步。
void f1(string str){
cout << str << endl;
}
class A{
public:
void operator()(string str){
cout << str << endl;
}
};
int main(){
thread th1(f1, "我是第一个子线程");//传递函数
auto f2 = [](string str){cout << str << endl;};
thread th2(f2, "我是第二个子线程");//传递lambda表达式
A a;
thread th3(a, "我是第三个子线程");//传递重载了调用运算符()的类对象
th1.join();
th2.join();
th3.join();
return 0;
}
3、拷贝构造函数
thread (const thread&) = delete;
拷贝构造函数被delete
,不可用。
4、移动构造函数
thread (thread&& x) noexcept;
将线程x移交给新线程,需要使用std::move()
函数。此后,x不在拥有该线程,且处于non-joinable
状态,但可以再用赋值运算符将另一个线程移交给x。
int main(){
auto f = [](std::string str) { std::cout << str << std::endl; };
std::thread th1(f, "我是子线程");
std::thread th2(std::move(th1));//移交给新线程th2
std::thread th3(f, "我也是子线程");
if(th1.joinable() == false){//th1为non-joinable状态
cout << "th1 is non-joinable" << endl;
}
th1 = move(th3);
if(th1.joinable() == true){//th1为joinable状态
cout << "th1 is joinable" << endl;
}
th2.join();
th1.join();
return 0;
}
析构函数
~thread();
析构函数销毁thread
对象。当线程是joinable
时,销毁线程时将调用terminate()
函数。就是说对于一个joinable
子线程,在其结束之前,主线程应该调用join
函数等待其运行结束。
成员函数
thread::get_id
id get_id() const noexcept;
该函数返回调用线程的ID,返回值类型为thread::id
。thread::id
是一个类,当调用线程是joinable
时,id
类对象中的值是独一无二的、可标识一个线程的。当调用线程是non-joinable
时,id
类对象中的值是0。这个类支持==
和<
操作,但没有转换成整型的操作。若想与0比较,则可使用其默认构造函数生成一个临时对象,如std::thread::id()
。
int main(){
auto f = []{std::cout << "child" << std::endl;};
std::thread th1(f);
if(th1.get_id() == std::thread::id()){
std::cout << "子线程的id为0" << std::endl;
}
if(th1.get_id() == std::this_thread::get_id()){
std::cout << "子线程和主线程ID一样" << std:endl;
}
return 0;
}
thread::detach
void detach();
从主线程中分离子线程,允许子线程独立运行。分离之后,主线程和子线程会继续运行,分离后的子线程由操作系统负责回收其资源,其属性变为non-joinable
。
int main(){
auto f = []{std::cout << "该线程id: " << std::this_thread::get_id() << std::endl;};
std::thread th1(f);
th1.detach();//分离线程后,主线程不需要等待其运行结束
return 0;
}
thread::join
void join();
当子线程属性为joinable
时,主线程需要调用join
函数等待子线程运行结束,并回收其占用的资源,待子线程结束后,join
函数返回,子线程变为non-joinable
,且id为0。
int main(){
std::thread th1([] {std::cout << "第一个线程" << std::endl; });
th1.join();
if (th1.joinable() == false) {
std::cout << "第一个线程是non-joinable,其id为 " << th1.get_id() << std::endl;
}
th1 = std::move(std::thread([] {std::cout << "第二个线程" << std::endl; }));
th1.join();
if (th1.joinable() == false) {
std::cout << "第二个线程是non-joinable,其id为 " << th1.get_id() << std::endl;
}
return 0;
}
thread::joinable
bool joinable() const noexcept;
判断线程是否joinable
,是joinable
则返回true
,否则返回false
。
若线程属于以下三种,则是non-joinable
:
- 默认构造函数构造的对象;
- 线程已经被移动,即使用
std::move
函数来初始化另一个对象,或者用赋值运算符给另一个对象赋值。 - 线程的
join
或detach
成员函数已经调用过了。
std::thread th1;//默认构造函数,th1是non-joinable
std::thread th2([]{std::cout << "hello,world!" << std::endl;});
th1 = std::move(th2);//th2被赋值给th1,th2变为non-joinable,th1变为joinable
std::thread th3(std::move(th1));//使用移动构造函数将th1移动给th3,th1变为non-joinable,th3变为joinable
th3.detach();//调用了detach函数,th3变为non-joinable
std::thread th4([]{std::cout << "hello,world!" << std::endl;});
th4.join();//调用了join函数,待其返回后,th4变为non-joinable
joinable状态和非joinable状态
处于joinable
状态的线程在运行结束时不会自动释放占用的堆栈和线程描述符等资源,需要主线程调用join
函数等待子线程运行结束并回收其资源。若joinable
状态的子线程运行结束,而主线程没有调用join
函数等待并释放其资源,就会报错。
int main(){
std::thread th1(f);//创建子线程
std::this_thread::sleep_for(std::chrono::millisecond(100));//主线程睡眠100ms,此时子线程上处理机运行
std::cout << "我是主线程" << std::endl;//执行该语句时,子线程肯定已经运行结束了
return 0;//因为子线程是joinable状态,但主线程没调用join等待并回收其资源,发生错误,错误代码3
}
处于non-joinable
状态的线程(即分离状态),由操作系统负责回收其占用的资源,主线程不需要等待其运行结束,可用detach
函数使线程变为non-joinable
状态。
int main(){
std::thread th1(f);//创建子线程
th1.detach();//子线程变为non-joinable状态,即分离状态
std::this_thread::sleep_for(std::chrono::millisecond(100));//主线程睡眠100ms,此时子线程上处理机运行
std::cout << "我是主线程" << std::endl;//执行该语句时,子线程肯定已经运行结束了
return 0;//因为子线程是non-joinable状态,由操作系统回收其资源,正常结束
}
thread::native_handle(难搞)
native_handle_type native_handle();
该成员函数需要库函数的支持,返回的值用于获取与线程相关的具体应用信息。
thread::operator=
thread& operator= (thread&& rhs) noexcept;//移动赋值运算符
thread& operator= (const thread&) = delete;//不可用
移动赋值运算符将一个线程rhs移动赋值给接收线程,之后rhs线程变为non-joinable
且不执行任何操作。接收线程不能为joinable
。
移动赋值运算符返回一个*this
的引用,即返回接收rhs的线程的引用,因此可以实现连续赋值。
int main(){
std::thread th1([]{std::cout << "hello,world!" << std::endl;});
std::thread th2;
std::thread th3;
th2 = std::move(th3);//没问题,接收线程th2原本就是non-joinable。因为th3是non-joinable,所以接收线程th2还是non-joinable
th1 = std::move(th2);//错误,th1是joinable
th3 = std::move(th2 = std::move(th1));//连续赋值
th3.join();
return 0;
}
thread::swap
void swap (thread& x) noexcept;
交换线程x和当前线程。
std::thread th1([] {std::cout << "th1线程" << std::endl; });
std::thread th2([] {std::cout << "th2线程" << std::endl; });
std::cout << "th1 id:" << th1.get_id() << std::endl;
std::cout << "th2 id:" << th2.get_id() << std::endl;
th1.swap(th2);//交换th1和th2
std::cout << "th1 id:" << th1.get_id() << std::endl;
std::cout << "th2 id:" << th2.get_id() << std::endl;
th1.join();
th2.join();
执行结果为:
数据成员
thread::id
class thread::id;
id
是thread
类中的一个类数据成员,用于存放线程的线程描述符,即id。
thread::get_id()
和this_thread::get_id()
都返回一个id
类对象。
对于non-joinable
线程,相对于主线程来说,其线程id为0。
int main() {
std::thread th1([] {std::cout << "th1线程id为" << std::this_thread::get_id() << std::endl; });
std::cout << "分离之前,th1线程id为" << th1.get_id() << std::endl;
th1.detach();
std::cout << "分离之后,th1线程id为" << th1.get_id() << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(100));//等待100ms,避免主线程先结束
return 0;
}
运行结果为:
thread::native_handle_type(难搞)
typedef /* implementation-defined */ native_handle_type;
该数据成员需要库函数支持才能使用。
静态成员函数
thread::hard_ware_concurrency
static unsigned hardware_concurrency() noexcept;
检测硬件并发性,返回硬件线程上下文的数量,简单理解就是返回处理器核数。
int main(){
auto num = std::thread::hardware_concurrency();
std::cout << "我的处理器核数 " << num << std::endl;
return 0;
}
运行结果为:
友元函数
std::swap
void swap (thread& x, thread& y) noexcept;
交换两个线程x和y,作用与成员函数swap
一样。
std::thread th1([] {std::cout << "th1线程" << std::endl; });
std::thread th2([] {std::cout << "th2线程" << std::endl; });
std::cout << "th1 id:" << th1.get_id() << std::endl;
std::cout << "th2 id:" << th2.get_id() << std::endl;
std::swap(th1, th2);//交换线程th1和th2
std::cout << "th1 id:" << th1.get_id() << std::endl;
std::cout << "th2 id:" << th2.get_id() << std::endl;
th1.join();
th2.join();
运行结果为: