【C++】C++11之线程库

news2024/9/20 22:13:30


一、thread类

C++11 之前,涉及到多线程问题,都是和平台相关的,比如 windows linux 下各有自己的接 口,这使得代码的可移植性比较差 C++11 中最重要的特性就是对线程进行支持了,使得 C++ 并行编程时不需要依赖第三方库 ,而且在原子操作中还引入了原子类的概念。要使用标准库中的线程,必须包含< thread > 头文件。

  

我们可以参考下面文档:C++ thread类

   

1.1 thread类的构造方法

1、支持无参构造。构造一个空线程对象,由于没有关联的线程函数,所以不会直接运行。

2、支持可变参数构造。(最常用) 构造一个线程对象,并关联线程函数funargs1args2...为线程函数的参数。

#include<iostream>
#include<thread>

using namespace std;

void Add(int x, int y)
{
	cout << x + y << endl;
}

int main()
{
	int a = 10, b = 30;
	thread t1(Add, a, b);
	t1.join();
	return 0;
}

这里join函数的作用是让线程运行完进程进行回收。不然就会造成资源不回收,引发内存泄漏。 

3、不支持拷贝构造。

 

4、支持移动赋值。

   

1.2 其他函数接口

get_id:获取线程id,也是线程的唯一标识。get_id()的返回值类型为id类型,id类型实际为std::thread命名空间下封装的一个类,该类中包含了一个结构体:

typedef struct
{     /* thread identifier for Win32 */
     void *_Hnd; /* Win32 HANDLE */
     unsigned int _Id;
} _Thrd_imp_t;

join:等待线程回收分配给线程的资源。

joinable:用于判断是否需要回收线程资源。                  

detach:线程与主线程分离,彼此独立运行。两个线程继续,不会以任何方式阻塞或同步。请注意,当任何一个结束执行时,都会释放其自己的资源。

  

注意

1. 线程是操作系统中的一个概念, 线程对象可以关联一个线程,用来控制线程以及获取线程的状态
2. 当创建一个线程对象后,没有提供线程函数,该对象实际没有对应任何线程。
3. 当创建一个线程对象后,并且给线程关联线程函数,该线程就被启动,与主线程一起运行。
线程函数一般情况下可按照以下三种方式提供:
  • 函数指针
  • lambda表达式
  • 函数对象(仿函数)
class temp
{
public:
	void operator()()
	{
		cout << "thread t3" << endl;
	}
};

int main()
{
	thread t1(Add, 1, 2);
	thread t2([]() {cout << "thread t2" << endl; });
	temp t;
	thread t3(t);

	t1.join();
	t2.join();
	t3.join();
	return 0;
}
4. thread类是防拷贝的,不允许拷贝构造以及赋值,但是可以移动构造和移动赋值,即将一个线程对象关联线程的状态转移给其他线程对象,转移期间不意向线程的执行。
5. 可以通过jionable()函数判断线程是否是有效的,如果是以下任意情况,则线程无效
  • 采用无参构造函数构造的线程对象
  • 线程对象的状态已经转移给其他线程对象
  • 线程已经调用jion或者detach结束

    

2.3 this_thread命名空间

 get_id:用于获取线程id。

sleep_for:进程睡眠一段时间。

sleep_until:进程睡眠至某个时间。

由于不会特别常用,这里就不详细介绍,需要用时差文档即可:this_thread - C++ Reference (cplusplus.com)


二、mutex锁

2.1 mutex类

多线程最主要的问题是共享数据带来的问题(即线程安全) 如果共享数据都是只读的,那么没问题,因为只读操作不会影响到数据,更不会涉及对数据的修改,所以所有线程都会获得同样的数据。但是,当一个或多个线程要修改共享数据时,就会产生很多潜在的麻烦
比如:
我们在实现++操作的时候,看起来是一行代码,实际上底层汇编有三条。当我们进行到一半而时间片的时间到了,那么该线程就会被切走阻塞,让别的线程来使用cpu,而如果后来的线程也对a进行++操作,操作后再把原来的进程切换回来,原来的进程操作的还是原来的a,那么最后的结果就会出现问题。

案例代码:

int ret = 0;

void Func()
{ 
	int n = 10000;
	while (n--)
	{
		ret++;
	}
}

int main()
{
	thread t1(Func);
	thread t2(Func);

	t1.join();
	t2.join();

	cout << ret << endl;

	return 0;
}

 为了解决这个问题,引入了锁mutex来使得++操作一次完成。

 mutex类用到的主要两个函数就是:lock 和 unlock

mutex m;
int ret = 0;

void Func()
{ 
	int n = 10000;
	while (n--)
	{
		m.lock();
		ret++;
		m.unlock();
	}
}

int main()
{
	thread t1(Func);
	thread t2(Func);

	t1.join();
	t2.join();

	cout << ret << endl;

	return 0;
}

    

2.2 recursive_mutex

mutex类的锁是不能够递归加锁的,会出问题。为了适应这种情况,引入了recursive_mutex类。

 该类提供的函数接口和mutex类一样,但是允许一个线程多次加锁,来获得互斥对象的多个级别的所有权。

  

2.3 timed_mutex

相较于上面两种锁,timed_mutex锁增加了两个功能:try_lock_for 和 try_lock_until 

try_lock:能够在一定的时间范围内申请锁。如果当前锁未被申请,那么调用的线程就将取走锁;如果当前锁已经被申请了,那么就会返回false。

try_lock_until:尝试申请锁知道某个时间点。


三、原子性操作库(atomic)

虽然加锁可以解决,但是加锁有一个缺陷就是:只要一个线程在对 sum++ 时,其他线程就会被阻塞,会影响程序运行的效率,而且锁如果控制不好,还容易造成死锁。因此C++11中引入了原子操作。所谓原子操作:即不可被中断的一个或一系列操作, C++11 引入的原子操作类型,使得线程间数据的同步变得非常高效。

该类的使用需要包含头文件<atomic>

 

我们下面看atomic类的构造方法:

可以看到:支持无参构造和列表初始化,但是不能拷贝。

原子类型通常属于 " 资源型 " 数据,多个线程只能访问单个原子类型的拷贝,因此 C++11 中,原子类型只能从其模板参数中进行构造,不允许原子类型进行拷贝构造、移动构造以及 operator= 等,为了防止意外,标准库已经将 atmoic 模板类中的拷贝构造、移动构造、赋值运算符重载默认删除掉了。
  
使用案例:
atomic<int> a;
void func()
{
	int n = 10000;
	while (n--)
	{
		a++;
	}
}

int main()
{
	thread t1(func);
	thread t2(func);

	t1.join();
	t2.join();

	cout << a << endl;

	return 0;
}

C++11 中, 程序员不需要对原子类型变量进行加锁解锁操作,线程能够对原子类型变量互斥的 访问 。更为普遍的,程序员可以使用 atomic 类模板,定义出需要的任意原子类型

四、利用RAII机制管理锁

4.1 lock_guard

这是一个C++中定义的用来管理锁的类,在构造对象时候加锁,析构对象的时候解锁。

实现代码:

template<class _Mutex>
class lock_guard
{
public:
	explicit lock_guard(_Mutex& _Mtx)
		:_MyMutex(_Mtx)
	{
		_MyMutex.lock();
	}

	lock_guard(_Mutex& _Mtx, adopt_lock_t)
		:_MyMutex(_Mtx)
	{}

	~lock_guard() _NOEXCEPT
	{
		_MyMutex.unlock();
	}

	lock_guard(const lock_guard&) = delete;
	lock_guard& operato = (const lock_guard&) = delete;
private:
	_Mutex _MyMutex;
};

通过上述代码可以看到,lock_guard类模板主要是通过RAII的方式,对其管理的互斥量进行了封,在需要加锁的地方,只需要用上述介绍的任意互斥体实例化一个lock_guard,调用构造函数成功上锁,出作用域前,lock_guard对象要被销毁,调用析构函数自动解锁,可以有效避免死锁问题lock_guard的缺陷:太单一,用户没有办法对该锁进行控制,因此C++11又提供了unique_lock。 

案例:

int a = 0;
mutex mx;

void func()
{
	int n = 10000;
	lock_guard<mutex> mt(mx);
	while (n--)
	{
		a++;
	}
}

int main()
{
	thread t1(func);
	thread t2(func);

	t1.join();
	t2.join();

	cout << a << endl;

	return 0;
}

     

4.2 unique_lock

lock_gard 类似, unique_lock 类模板也是采用 RAII 的方式对锁进行了封装,并且也是以独占所 有权的方式管理 mutex 对象的上锁和解锁操作,即其对象之间不能发生拷贝 。在构造 ( 或移动(move)赋值 ) 时, unique_lock 对象需要传递一个 Mutex 对象作为它的参数,新创建的unique_lock 对象负责传入的 Mutex 对象的上锁和解锁操作。 使用以上类型互斥量实例化 unique_lock 的对象时,自动调用构造函数上锁, unique_lock 对象销毁时自动调用析构函数解 锁,可以很方便的防止死锁问题。
  
lock_guard 不同的是, unique_lock 更加的灵活,提供了更多的成员函数:
  • 上锁/解锁操作locktry_locktry_lock_fortry_lock_untilunlock
  • 修改操作移动赋值、交换(swap:与另一个unique_lock对象互换所管理的互斥量所有权)、释放(release:返回它所管理的互斥量对象的指针,并释放所有权)
  • 获取属性owns_lock(返回当前对象是否上了锁)operator bool()(owns_lock()的功能相同)mutex(返回当前unique_lock所管理的互斥量的指针)

五、条件变量

我们先看一道题:两个线程交替打印0-100的数字,一个打印奇数,一个打印偶数。

我们通常情况下解题是while循环中用if条件判断来判断,一个线程t1判断奇数打印,一个线程t2判断偶数打印,然后打印完++。但是当我们打印t1奇数的时候,此时时间片切到t2,t2会不断的循环判断,直到时间片切回t1。这样就造成了CPU资源的浪费。

这里就要引入我们的条件变量:std::condition_variable、

条件变量中的 wait 和 notify_one 的接口能够实现进程的等待和唤醒。 使得进程避免因为不满足条件而一直循环判断,浪费资源。

 需要注意的是:

wait接口的参数是unique_lock类型。

有人会好奇为什么需要传一个锁进来呢?

因为条件变量操作不是原子性的,我们需要加锁保护,但是我们加了锁让线程等待,但是其他线程因为申请不到锁也会进入阻塞,那么不就死循环了吗?

其实并不是的,wait操作之所以需要传一个锁进来,就是因为wait操作的同时,会将锁释放,让其他线程能够申请到锁,直到用notify_one来唤醒线程的时候,才会重新持有锁。

   

有了条件变量,我们可以让进程在不满足条件的时候进行等待,在满足条件之后再唤醒进程运行。

案例代码:

int main()
{
	int a = 0;
	condition_variable cv;
	mutex mt;
	//打印奇数
	thread t1([&]()
		{
			while (a <= 100)
			{
				unique_lock<mutex> lock(mt);
				if (a % 2 == 0)
				{
					cv.wait(lock);
				}
				cout << "t1->" << a << endl;
				++a;

				cv.notify_one();
			}
		});

	thread t2([&]()
		{
			while (a <= 100)
			{
				unique_lock<mutex> lock(mt);
				if (a % 2 == 1)
				{
					cv.wait(lock);
				}
				cout << "t2->" << a << endl;
				++a;

				cv.notify_one();
			}
		});

	t1.join();
	t2.join();
	return 0;
}

我们这里就发现问题了,怎么会打印出101来呢?

原因出在这里:

 因此我们只需要把t1时的循环条件<= 改成 < 即可,这样,在100的时候进不去循环了,自然后面的操作也就不会执行了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/733583.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

刚发offer,明天就让上班!原公司离职手续还没办,说晚两天报到,hr竟取消offer,录用其他人!...

这年头找工作有多难&#xff1f;一位网友吐槽&#xff1a;一瞬间从天堂掉进地狱&#xff01; 楼主说&#xff0c;hr今天刚发了offer&#xff0c;就让明天来上班。自己说明天不可能&#xff0c;原公司离职手续还没办完&#xff0c;hr就取消了offer&#xff0c;转而录用了其他候选…

MySQL基本查询与内置函数

目录 聚合函数 分组查询 内置函数 日期函数 字符串函数 数学函数 聚合函数 COUNT&#xff1a;返回查询到的数据的数量 SUM&#xff1a;返回查询到的数据的总和&#xff08;数字&#xff09; AVG&#xff1a;返回数据的平均值 MAX&#xff1a;返回查询到的数据的最大值 MIN&a…

毫米波雷达系列 | 毫米波雷达测角原理-补档

毫米波雷达测角原理 角度分辨率 当估算角度分辨率最少需要两个RX天线&#xff0c;利用的是物体相对每个天线的差分距离。 假设雷达具有一个发射天线TX和两根接受天线RX1和RX2之间的间距为d&#xff0c; θ \theta θ为目标相对天线的角度&#xff0c;那么接受天线之间的相位…

OpenCV 入门教程:颜色空间转换

OpenCV 入门教程&#xff1a;颜色空间转换 导语一、颜色空间的基本概念1.1 RGB颜色空间1.2 灰度颜色空间1.3 其他颜色空间 二、颜色空间转换三、示例应用3.1 提取图像的色彩通道3.2 调整图像的亮度和对比度 总结 导语 在图像处理和计算机视觉领域&#xff0c;颜色空间转换是一…

学无止境·MySQL③

单表查询 题一创建表并插入数据薪水修改为5000将姓名为张三的员工薪水修改为3000元将姓名为李四的员工薪水修改为4000元&#xff0c;gener改为女 题一 1.创建表&#xff1a; 创建员工表employee&#xff0c;字段如下&#xff1a; id&#xff08;员工编号&#xff09;&#xff…

CVE 官网提交流程

CVE 官网提交流程 一、进入官网申请 https://cveform.mitre.org/ 选择申请CVE ID即可。 Vulnerability type翻译&#xff08;chatgpt-3.5&#xff09;buffer overflow缓冲区溢出是一种软件漏洞&#xff0c;攻击者在向缓冲区写入超出其容量的数据时&#xff0c;可能导致数据覆…

一文搞懂常见的加密算法

加密算法在互联网技术领域中几乎是无处不在&#xff0c;而密码学也是网络安全的重要基础&#xff0c;这篇文章我们就一起来学习下常见的加密算法。 1 为什么要研究加密算法&#xff1f; 在技术方面&#xff0c;加密算法的研究具有重要的意义&#xff0c;主要体现在以下几个方…

【LeetCode】HOT 100(25)

题单介绍&#xff1a; 精选 100 道力扣&#xff08;LeetCode&#xff09;上最热门的题目&#xff0c;适合初识算法与数据结构的新手和想要在短时间内高效提升的人&#xff0c;熟练掌握这 100 道题&#xff0c;你就已经具备了在代码世界通行的基本能力。 目录 题单介绍&#…

从零开始 Spring Boot 66:JPA 查询参数

从零开始 Spring Boot 66&#xff1a;JPA 查询参数 图源&#xff1a;简书 (jianshu.com) JPA 的查询参数分为两种&#xff1a; 命名参数&#xff08;Named Parameters&#xff09;位置参数&#xff08;Positional Parameters&#xff09; 类似于 Python 中的函数的位置参数和…

Linux的网络配置与远程连接与文件传输

&#xff08;该图由AI绘制 关注我 学习AI画图&#xff09; 目录 网络配置 1、ifconfig查看网络信息 2、与网卡相关的配置文件 3、查询计算机的网络状态 4、systemctl启动/重启/停止网络 Linux远程连接与文件传输 1、为什么需要远程连接 2、SSH协议 3、sshd服务 4、…

2023CCF CAT- 热身赛

NOIP普及组 字符串 排序2017 动态规划 递推 USACO 2001 贪心 牛客小白月赛12 说实话还是很喜欢打比赛&#xff0c;喜欢AC的感觉&#xff0c;但是这玩意咋越来越难了那。。。。。 扎心了&#xff0c;不是~~~~~ 当个爱好吧&#xff0c;还是很喜欢当年打比赛和队友相视一笑的样子…

宇凡微2.4g无线合封芯片,高集成内置九齐单片机

2.4GHz是指一段频率范围&#xff0c;用来表示无线通信中的特定频率范围。在无线传输产品和设备中&#xff0c;2.4GHz被广泛应用&#xff0c;用于传输固定频率的波形以实现接收和发射功能。 需要注意的是&#xff0c;2.4GHz和蓝牙功能在基本上是相似的&#xff0c;但并不是所有…

前端面试刷题整理

第一题&#xff1a;es6 class语法 题目&#xff1a;现有三种菜单&#xff0c;button属性&#xff0c;select属性&#xff0c;model属性 class Mune{constructor(title,icon){this.title titlethis.icon icon}isDisabled(){return false}exec(){} } class Button extends Mun…

LeakTracer代码学习(1)

项目中有的时候会产生内存泄漏&#xff0c;以往的经验&#xff0c;检测工具更倾向于使用LeakTracer进行检测泄漏问题&#xff0c;但是直接使用会有些问题&#xff0c;比如堆栈不全都是??等问题&#xff0c;该专题希望自己能够坚持将LeakTracer的源码梳理清楚&#xff0c;以供…

ModaHub魔搭社区:RESTful API 的方式访问全球领先的向量数据库Milvus

目录 Insert CreateIndex and Load Search or Query 作为全球领先的开源向量数据库,Milvus 一直致力于满足不同用户的场景和需求,聆听社区的声音。 最近, 我们发现,很多用户的数据中 常常包含各种不确定类型的数据,也有用户提出希望以 RESTful API 的方式访问 Milvus。…

【HarmonyOS】【FAQ】HarmonyOS应用开发相关问题解答(三)

贴接上回。。。 【往期FAQ参考】 【HarmonyOS】【FAQ】HarmonyOS应用开发相关问题解答&#xff08;一&#xff09; 【HarmonyOS】【FAQ】HarmonyOS应用开发相关问题解答&#xff08;二&#xff09; 【本期FAQ】 1、第一次调用geolocation.getCurrentLocation()接口&#xff…

一份非常牛逼的计算机相关技术资料整理

最近发现GitHub上一个非常牛逼的项目。作者收录了一整套 计算机相关的技术资料整理。 收录内容包括&#xff0c;但不仅仅包括&#xff0c;比如比较实用的计算机相关技术书籍&#xff0c;可以在短期之内入门的简单实用教程、一些技术网站以及一些写的比较好的博文。真的得给作者…

postman测试传参格式

postman测试传参格式 创建User实体 import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor;Data NoArgsConstructor AllArgsConstructor public class User {private Integer id;private String name; }接口参数是集合 PostMapping("…

大规模语言模型剪枝又一力作,比SparseGPT还快300倍!

©Paperweekly 原创 作者 | An. 单位 | 中科院自动化所 研究方向 | 计算机视觉、模型压缩 DenseNet、ConvNeXt、Network Slimming 一作刘壮研究员的剪枝新工作&#xff0c;针对 LLMs 特点设计的极低成本无需微调的剪枝算法&#xff0c;耗时接近幅值剪枝&#xff0c;性能表…

基于Spring Boot的高校实验室信息管理系统设计与实现(Java+spring boot+MySQL+VUE)

获取源码或者论文请私信博主 演示视频&#xff1a; 基于Spring Boot的高校实验室信息管理系统设计与实现&#xff08;Javaspring bootMySQLVUE&#xff09; 使用技术&#xff1a; 前端&#xff1a;html css javascript jQuery ajax thymeleaf 后端&#xff1a;Java springboot…