c++多线程库手册学习笔记(一)

本文是C++多线程库的学习笔记,主要介绍了C++中创建和管理线程的基本概念。线程类型thread、互斥锁mutex和条件变量condition_variable是多线程编程的基础。thread用于跟踪和管理线程,mutex确保资源的安全访问,condition_variable则帮助协调线程间的同步。文章详细讲解了thread的创建、参数传递以及线程对象的特性,强调了线程join的重要性,并预告了对mutex的总结。
摘要由CSDN通过智能技术生成

好久不写博客了,一方面是觉得比较花时间,另一方面觉得也没啥好写的,不过最近看cppreference上关于c++多线程的支持,觉得有必要整理整理,就来写一写好了。

一、基本的多线程需要的东西:
对于多线程代码而言,我觉得最基本需要有以下一些类型:

  1. thread类型
  2. 互斥锁mutex
  3. 条件变量condition_variable

thread类型用来跟踪管理一个线程,互斥锁提供多个线程共享资源的安全访问机制,条件变量可以使得多个协同线程按顺序访问共享资源,减小CPU资源损耗。对于以上几种基本类型,c++都提供了对应的类型支持,分别是:

  1. thread
  2. mutex 等若干种
  3. condition_variable

二、thread

该类型在头文件thread中定义,包含于std命名空间:

#include <thread>

1、创建线程:
c++支持任何可以像函数一样被调用的对象作为线程的入口函数,具体为:

  • 函数
  • 重载了 () 运算符的类型的实例
#include <thread>
#include <iostream>
void function_default()
{
	std::cout<<"This is function_default.\n";
}

void function_para1(int x)
{
	std::cout<<"This is function with one para: "<<x<<std::endl;
}

class AAA
{
public:
	void operator()(int x) const{
		std::cout<<"This is an object of AAA: "<<x<<std::endl;
	}
};

int main()
{
	std::thread t1(function_default);
	std::thread t2(function_para1,10);
	AAA obj;
	std::thread t3(obj,20);
	t1.join();
	t2.join();
	t3.join();
	return 0;
}

2、线程对象创建机制:
c++线程对象在构造时,先将传入的参数赋值到新线程的独立空间中,然后此时原线程将继续向下执行,新线程在自己的空间中自动执行。

  • 如果线程需要原线程中的变量的左值引用作为参数,则应该显式的以左值引用方式传递,否则thread的构造函数依然是机械式的赋值该参数,然后传递给线程函数,起不到原先预想的作用:
void func(int& x){
	...
}
int main(){
	int p=10;
	std::thread t1(func,std::ref(p));
	...
}
  • 因为该传参机制,传参时还要注意有可能引发临时变量引用前被销毁的风险:
void func_thread(std::string str){
	...
}
std::thread func(){
	char a[]="This is a string";
	return std::thread(func_thread,a);
}

在函数func中用a数组作为参数创建线程并返回,线程构造函数首先将a的地址赋值到线程空间,然后func将返回,此时a的空间以不合法,然而线程需要访问a的空间以构造str,此时的行为是未知的,解决方法是传参时完成类型转换:

std::thread func(){
	char a[]="This is a string";
	return std::thread(func_thread,std::string(a));
}

3、线程对象的性质

  • 不可复制
  • 可移动
    thread类型的复制构造函数和赋值运算符被显式删除。但支持移动语义,可在各个thread对象间转移所有权。但貌似承接转移的对象不能拥有实例线程(测试过)。
void function_default()
{
	while(1)
		std::cout<<"This is function_default.\n";
}

void function_para1(int x)
{
	while(1)
		std::cout<<"This is function with one para: "<<x<<std::endl;
}

int main()
{
	std::thread t1(function_default);
	std::thread t2(function_para1,10);
	std::thread t3=std::move(t1);
	//t2=std::move(t1) 运行时报错
	t1.join();
	t3.join();
	//因为t2将线程所有权转交给了t3,所以t2不用join了,其joinable()为false,也不具备任何线程的所有权,空壳。
	return 0;
}

4、线程的自动join
在c++标准中,一个线程若为joinable的,则必须在线程对象被销毁前join它,无论是正常退出定义域还是异常退出都需要这么做,所以最好可以使用一个管理类对其封装。
貌似c++没有为thread提供类似的功能,c++20新增的jthread貌似提供了这种功能,但在我的机器上g++ 7.4.0没有提供类似的支持貌似。但这里可以自己写一个封装类进行管理。

class unique_thread
{
	std::thread p;
public:
	explicit unique_thread(std::thread&& t):p(std::move(t)){}
	explicit unique_thread(unique_thread&& t):p(std::move(t.p)){}
	unique_thread& operator=(unique_thread&& t)
	{
		if(p.joinable())
			p.join();
		p=std::move(t.p);
		return *this;
	}
	~unique_thread()
	{
		if(p.joinable())
			p.join();
	}
	......//其他必要的成员函数
public:
	unique_thread(const unique_thread&)=delete;
	unique_thread& operator=(const unique_thread&)=delete;
};

int main()
{
	unique_thread t1{std::thread(function_default)};
	//unique_thread t1(std::thread(function_default));   测试表明不能写成这种形式,应该是格式被编译器错误解析成了函数调用,改成{}形式的构造函数传参可以解决这个问题,也许养成使用{}进行对象构造的习惯是个不错的选择,但我目前还是习惯(), lol。
	unique_thread t2(std::thread(function_para1,10));
	return 0;
}

改天看看如何测试jthread类。

下一章来总结mutex

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值