C++语言级别的多线程

news2024/11/17 17:46:07

1.线程概念

好处:可以跨平台:windows / linux / mac
线程间的互斥:mutex / lock_quard / unique_lock
线程间的通信:condition_variable
atomic : 原子类型 基于CAS操作的原子类型 线程安全的
sleep_for :睡眠

在这里插入图片描述
C++语言层面调用thread,在windows操作系统上底层调用的是createThread,在linux下调用的是pthread_create。可以通过编译器的编译,加一个宏,可以识别当前的操作系统,可以通过适配在语言层面编写的thread底层自动调用相应的函数。本质上还是在调用操作系统线程创建的API。就是语言层面加了一层封装,对于开发者更友好

2.线程的基本操作

线程内容:

  • 1.怎么创建启动一个线程
    std: :thread定义一个线程对象,传入线程所需要的线程函数和参数,线程自动开启
  • 2.子线程如何结束
    子线程函数运行完成,线程就结束了
  • 3.主线程如何处理子线程
    • t.join() : 等待t线程结束,当前线程继续往下运行
    • t.detach() : 把t线程设置为分离线程,主线程结束,整个进程结束,所有子线程都自动结束了!
#include<iostream>
#include<thread>
using namespace std;

void threadHandle1()
{
	//让子线程睡眠2秒
	std::this_thread::sleep_for(std::chrono::seconds(2));

	cout << "hello thread1!" << endl;
}

int main()
{
	//创建了一个线程对象,传入一个线程函数,新线程就开始运行了
	thread t1(threadHandle1);
	//主线程等待子线程结束,主线程继续往下运行
	//t1.join();
	t1.detach();

	cout << "main run over!" << endl;
	return 0;
}

3.线程间互斥

mutex

只能用在简单的临界区代码段的互斥操作中

多线程程序
竞态条件:多线程程序执行的结果是一致的,不会随着CPU对线程不同的调用顺序,而产生不同的运行结果,

#include<thread>
#include<list>
#include<mutex>
using namespace std;


static int ticketcount = 100;
std::mutex mtx;//全局的一把互斥锁

//模拟买票的线程函数
void sellTicket(int index)
{
	mtx.lock();//此种加锁相当于只有一个窗口在买票
	while (ticketcount >= 1)//锁+双重判断
	{
		if (ticketcount > 0)
		{
			//临界区代码段-》原子操作=》线程间互斥操作了=》mutex
			cout << "窗口:" << index << "  卖出第:" << ticketcount << " 张票!" << endl;
				ticketcount--;
			
		}
		std::this_thread::sleep_for(std::chrono::milliseconds(100));
	}
	mtx.unlock();
}
int main()
{
	list<std::thread>tlist;
	for (int i = 1; i <= 3; i++)
	{
		tlist.push_back(std::thread(sellTicket, i));
	}

	for (thread& t : tlist)
	{
		t.join();
	}
	cout << "所有窗口买票结束!" << endl;
	return 0;
}
#include<thread>
#include<list>
#include<mutex>
using namespace std;


static int ticketcount = 100;
std::mutex mtx;//全局的一把互斥锁

//模拟买票的线程函数
void sellTicket(int index)
{
	while (ticketcount >= 1)//锁+双重判断
	{
		mtx.lock();
		if (ticketcount > 0)
		{
			//临界区代码段-》原子操作=》线程间互斥操作了=》mutex
			cout << "窗口:" << index << "  卖出第:" << ticketcount << " 张票!" << endl;
				ticketcount--;
		}
		mtx.unlock();
		std::this_thread::sleep_for(std::chrono::milliseconds(100));
	}
}

lock_quard

mutex最好不要手动加锁和解锁。因为临界区代码一旦执行错误,或者return了,那么获取的锁就无法释放了,一个线程不释放锁,其他线程不能获取锁,所以整个进程就是死锁,所以需要采取智能指针的思想(栈上对象出作用域都会被析构):lock_quard / unique_lock
保证了所有线程都能释放锁,有效防止了死锁问题的发生

缺点:delete了赋值和拷贝构造,所以不可能用在函数参数传递或者返回过程中

static int ticketcount = 100;
std::mutex mtx;//全局的一把互斥锁

//模拟买票的线程函数
void sellTicket(int index)
{
	while (ticketcount >= 1)//锁+双重判断
	{
		{
			lock_guard<mutex>lock(mtx);
			if (ticketcount > 0)
			{
				//临界区代码段-》原子操作=》线程间互斥操作了=》mutex
				cout << "窗口:" << index << "  卖出第:" << ticketcount << " 张票!" << endl;
				ticketcount--;
			}
		}
		std::this_thread::sleep_for(std::chrono::milliseconds(100));
	
	}
}

unique_lock

提供了右值的赋值重载和右值拷贝构造,所以具有资源转移的功能,可以使用在函数调用过程中

在函数调用,参数传递过程中不能使用lock_quard,因为lock_quard不支持赋值和拷贝构造,此时就需要使用unique_lock

void sellTicket(int index)
{
	while (ticketcount >= 1)//锁+双重判断
	{
		unique_lock<std::mutex>lock(mtx);
		if (ticketcount > 0)
		{
			//临界区代码段-》原子操作=》线程间互斥操作了=》mutex
			cout << "窗口:" << index << "  卖出第:" << ticketcount << " 张票!" << endl;
			ticketcount--;
		}
		lock.unlock();
		std::this_thread::sleep_for(std::chrono::milliseconds(100));
	}
}

4.线程间同步通信机制

在这里插入图片描述
生产者—消费者模型:锁+条件变量

#include<iostream>
#include<thread>
#include<list>
#include<mutex>
#include<queue>//C++STL所有的容器都不是线程安全
#include<condition_variable>
using namespace std;

std::mutex mtx;//定义互斥锁,做线程间的互斥操作
std::condition_variable cv; //定义条件变量,做线程间的同步通信操作

//生产者生产一个物品,通知消费者消费一个;消费完了,消费者再通知生产者继续生产物品
class Queue
{
public:
	void put(int val)//生产物品
	{
		//lock_guard<mutex>lock(mtx);
		unique_lock<std::mutex>lck(mtx);
		while (!que.empty())
		{
			//que不空,生产者应该通知消费者消费,消费完了再继续消费
			//生产者进程应该1.进入阻塞状态,2.并且释放mtx,消费者可以获得锁
			
			cv.wait(lck);//通过条件变量wait进入等待状态,一进入等待状态就会把锁释放
		}
		que.push(val);
		cv.notify_all();//通知其它所有的线程,我生产了一个物品,你们赶紧消费吧
		//其它线程得到该通知,就会从等待状态=》阻塞状态=》获取互斥锁才能继续执行

		cout << "生产者 生产:" << val << " 号物品" << endl;
	}
	int get()//消费物品
	{
		//lock_guard<mutex>lock(mtx);
		unique_lock<std::mutex>lck(mtx);
		while (que.empty())
		{
			//que空,消费者应该通知生产者生产
			//进程应该1.进入阻塞状态,2.并且释放mtx
			
			cv.wait(lck);//通过条件变量wait进入等待状态,一进入等待状态就会把锁释放
		}
		int val = que.front();
		que.pop();
		cv.notify_all();//通知其它线程我消费完了,赶紧生产吧

		cout << "消费者 消费:" << val << " 号物品" << endl;
		return val;
	}
private:
	queue<int>que;
};

void producer(Queue* que)
{
	for (int i = 1; i <= 10; i++)
	{
		que->put(i);
		std::this_thread::sleep_for(std::chrono::milliseconds(100));
	}
}
void consumer(Queue* que)
{
	for (int i = 1; i <= 10; i++)
	{
		que->get();
		std::this_thread::sleep_for(std::chrono::milliseconds(100));
	}
}

int main()
{
	Queue que;
	thread t1(producer,&que);
	thread t2(consumer, &que);

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

	return 0;
}

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

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

相关文章

配置Debian11服务器安装SSH,创建新用户并允许远程SSH远程登录,并禁止root用户远程SSH登录

一、在 Debian 中添加 sudo 用户 1.创建新用户 首先&#xff0c;要创建用户&#xff0c;当前用户必须是 root 用户或者 sudo 用户。 使用下面adduser 命令创建一个用户名为test的sudo用户&#xff0c;按照提示输入密码&#xff0c;使用 adduser 命令&#xff0c;还会创建用户…

(三分钟)速览传统边缘检测算子

边缘检测的传统方法&#xff1a; 图像边缘是图像最基本的特征&#xff0c;所谓边缘(Edge) 是指图像局部特性的不连续性。灰度或结构等信息的突变处称之为边缘。例如&#xff0c;灰度级的突变、颜色的突变,、纹理结构的突变等。边缘是一个区域的结束&#xff0c;也是另一个区域…

如何在星巴克连接家中Windows台式机?(安卓,iOS, Windows, macOS配合frp公网iP实现)...

zhaoolee 最近热衷于和海外热心老哥们交换硬盘中的单机游戏资源(BT下载)&#xff0c;家中有Windows台式机&#xff0c; 适合长时间挂机下载BT资源&#xff0c;zhaoolee希望能随时连接到Windows台式机新增下载任务&#xff0c;安装体积超大的主机游戏。 另外&#xff0c;公司有一…

Docker常用命令 - 黑马学习笔记

Docker服务命令 # 启动docker服务 systemctl start docker # 停止docker服务 systemctl stop docker # 查看docker状态 systemctl status docker # 重启docker服务 systemctl restart docker # 设置docker开机自启动 systemctl enable dockerDocker镜像命令 # 查看镜像&#…

MySQL--》MySQL数据库以及可视化工具的安装与使用—保姆级教程

目录 数据库简介 MySQL数据库的安装 配置MySQL环境变量 MySQL数据库的启动与使用 MySQL图形化管理工具 Navicat Preminum工具的使用 数据库简介 大多数情况下&#xff0c;特别是企业级应用中&#xff0c;将数据保存到可掉电式存储设备中供以使用是非常重要的&#xff0c…

牛客2022跨年场

​ F题使用python&#xff0c;就是加了一个end \0&#xff0c;然后寄了好多。 A 猜群名 小沙为了这场元旦比赛绞尽脑汁&#xff0c;他现在在每个题目中藏入了一个字&#xff0c;收集所有的字&#xff0c;并将按照题号排列成一句话即可通过本题**!** 其次关于本场比赛难度预…

jmap 和jstack使用

jmap jmap是JDK提供的一个可以生成Java虚拟机的堆转储快照dump文件的命令行工具 1.查看整个JVM内存状态 jmap -heap [pid]2.查看JVM堆中对象详细占用情况 jmap -histo [pid]3.导出整个JVM 中内存信息&#xff0c;可以利用其它工具打开dump文件分析&#xff0c;例如jdk自带的…

Faster RCNN网络源码解读(Ⅸ) --- ROIAlign、TwoMLPHead、FastRCNNPredictor部分解析

目录 一、回顾以及本篇博客内容概述 二、代码解析 2.1 FasterRCNNBase类 2.1.1 forward正向传播 2.2 FasterRCNN类 2.2.1 roi_heads定义 2.3 TwoMLPHead类&#xff08;faster_rcnn_framework.py&#xff09; 2.4 FastRCNNPredictor类 2.5 RoIHeads类&#xff08;roi_…

JavaWeb:用户注册登录案例

1.1 用户登录 1.1.1 需求分析 用户在登录页面输入用户名和密码&#xff0c;提交请求给LoginServlet在LoginServlet中接收请求和数据[用户名和密码]在LoginServlt中通过Mybatis实现调用UserMapper来根据用户名和密码查询数据库表将查询的结果封装到User对象中进行返回在LoginSe…

用或不用大O来优化代码(选择排序)

本文内容借鉴一本我非常喜欢的书——《数据结构与算法图解》。学习之余&#xff0c;我决定把这本书精彩的部分摘录出来与大家分享。 目录 写在前面 1.选择排序 2.选择排序实战 3.选择排序的实现 4.选择排序的效率 5.忽略常数 6.大O的作用 7.总结 写在前面 大 O 是一…

Java面向对象详解(下)

文章目录&#x1f4d6;前言&#xff1a;&#x1f3c5;封装• 封装的概念• 封装的好处• 封装的核心理解&#x1f3c5;继承• 继承的概念•继承的特点● 何时使用继承&#xff1f;● 继承的形式● 继承的传递性● 继承的构造方法&#x1f9f8;super关键字&#x1f387;用途&…

【QT开发笔记-基础篇】| 第五章 绘图QPainter | 5.14 平移、旋转、缩放

本节对应的视频讲解&#xff1a;B_站_视_频 https://www.bilibili.com/video/BV1te4y1L7Mu 本节讲解平移、旋转、缩放这些变换操作 1. 关联信号槽 首先&#xff0c;在 widget.cpp 的构造中&#xff0c;为 “变换” 复选框&#xff0c;关联信号槽 // 平移、旋转、缩放 conn…

leetcode 2439. 最小化数组中的最大值

给你一个下标从 0 开始的数组 nums &#xff0c;它含有 n 个非负整数。 每一步操作中&#xff0c;你需要&#xff1a; 选择一个满足 1 < i < n 的整数 i &#xff0c;且 nums[i] > 0 。将 nums[i] 减 1 。将 nums[i - 1] 加 1 。 你可以对数组执行 任意 次上述操作&…

程序的环境与预处理 程序的编译与链接

目录 1.程序的翻译环境和执行环境 ​编辑 2.编译链接 运行环境 3.预处理 预定义符号 #define #与## 带副作用的宏参数 宏和函数的对比 命名约定 ​编辑 #undef​编辑 命令行定义 ​编辑 条件编译 文件包含 嵌套文件包含 4.其他预处理指令 1.程序的翻译环境和…

新年伊始,和大家聊聊鲜枣课堂的未来

大家好&#xff0c;我是小枣君。时间过得很快&#xff0c;转眼之间&#xff0c;2022年已经结束了。回首这一年&#xff0c;感觉自己一直都在忙&#xff0c;却想不起来到底忙了些什么。这一年&#xff0c;我的生活和工作节奏&#xff0c;一直都是混乱的。这里面&#xff0c;既有…

罗振宇2023“时间的朋友”跨年演讲原版PPT(附下载)

省时查报告-专业、及时、全面的行研报告库省时查方案-专业、及时、全面的营销策划方案库【免费下载】2022年11月份热门报告盘点2023年&#xff0c;如何科学制定年度规划&#xff1f;《底层逻辑》高清配图清华大学256页PPT元宇宙研究报告.pdf&#xff08;附下载链接&#xff09;…

软件测试[用例篇]

一. 回顾测试用例 1.测试用例基本要素 测试用例&#xff08;Test Case&#xff09;是为了实施测试而向被测试的系统提供的一组集合。 这组集合包含&#xff1a;测试环境、操作步骤、测试数据、预期结果等要素。 2.测试用例好处 测试用例可以提高测试效率&#xff08;可以减…

省时省力,高速收费站无线组网解决方案

一、行业背景随着我国高速公路里程数的不断增加&#xff0c;科技水平的不断进步&#xff0c;智能化的高速公路收费站趋势在不断的加强。例如&#xff1b;高速公路收费站智能备份系统&#xff0c;通常情况下收费站、路段分中心和省联网中心之间是需要传输收费数据记录流水、清账…

【1801. 积压订单中的订单总数】

来源&#xff1a;力扣&#xff08;LeetCode&#xff09; 描述&#xff1a; 给你一个二维整数数组 orders &#xff0c;其中每个 orders[i] [pricei, amounti, orderTypei] 表示有 amounti 笔类型为 orderTypei 、价格为 pricei 的订单。 订单类型 orderTypei 可以分为两种&…

关于el-time-picker使用错误的记录

之前在紧急参与一个PC管理后台的项目&#xff0c;项目的基础架子是花裤衩大佬的vue-element-admin()vue2版本),。其中有一个需求是列表数据中数据回显时候&#xff0c;有关时间部分的数据在回显/编辑的情况下&#xff0c;提交时获取的值有问题。虽然后面解决了&#xff0c;但还…