C++多线程编程之thread类

参考www.cpluscplus.com

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::idthread::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函数来初始化另一个对象,或者用赋值运算符给另一个对象赋值。
  • 线程的joindetach成员函数已经调用过了。
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;

idthread类中的一个类数据成员,用于存放线程的线程描述符,即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();

运行结果为:
在这里插入图片描述

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值