Cpp多线程(一)

news2024/11/28 8:28:28

一、基本概念

1、程序是一段静态代码;进程是正在运行的程序;线程则是程序内部的执行路径。

上面这张图就解释了线程和多线程的意义。

2、若一个程序在同一时间执行多个线程,便是支持多线程的。一个进程中的多个线程共享相同的内存单元/内存地址空间。

二、两种形式的多线程

1、单CPU内核的多线程

单个CPU以极高的频率轮流执行多个线程的运算,各个线程雨露均沾。

2、多CPU内核的多线程

可以做到真正的并行运算

所以,现在我们所说的软件意义上的线程和硬件意义上的线程(如我的i7就是4核心8线程)并不是同一个概念。单个CPU内核也可以模拟出多个线程。对于一般的家用电脑,运行大约100个线程是可行的。

三、Cpp的多线程

在C++11标准之前,C++多线程需要借助操作系统提供的API,如Linux的pthread.h库和Windows的<windows.h>库。C++11提供了语言层面上的多线程,包含在头文件<thread>中。它解决了跨平台的问题。以下的各个库均为C++11标准为多线程所设计的:

  1. thread
  2. mutex
  3. atomic
  4. condition_variable
  5. future 

四、创建线程

创建线程时,必须把需要分开执行的程序封装在不同的代码块里,也就是函数里。对于线程,C++用类来描述,创建了一个Thread类的对象,即是新增了一个线程。

比如说我要分别执行两个冒泡排序,我便先写了一个冒泡排序的函数。

void bubblesort(int* array,int len)
{
	for(int i=1;i<len;i++)
	{
		for(int j=0;j<len-i;j++)
		{
			if (array[j]>array[j+1])
			{
				int temp=array[j];
				array[j]=array[j+1];
				array[j+1]=array[j];
			}
		}
	}
}

有下面两种创建线程的方法:

std::thread thread1(bubblesort,array1,LEN);
thread1.join();

1、创建线程thread1,运行函数bubblesort。对于thread构造函数,其第一个参数为所执行函数的名称(可能是引用),后面的参数则为所执行函数的参数。

std::thread (bubblesort,array1,LEN).join();

2、直接创建线程(无名称),并以规定的方式运行程序。

下面给出创建线程运行的例子,以及单线程运行的对比程序。两程序输出结果均为排序算法运行时间。读者可以自行验证,多线程的效率确实大有优势!

多线程:

#include <iostream>
#include <thread>
#include <ctime>
#include <cstdlib>

#define LEN 100000

void bubblesort(int* array,int len);

int main(void)
{
	int* array1;
	int* array2;
	array1=new int[LEN];
	array2=new int[LEN];
	srand(time(0));
	
	for (int i=0;i<LEN;i++)
	{
		array1[i]=(rand()%10000)*(rand()%10000);
		array2[i]=(rand()%10000)*(rand()%10000);
	}
	
	
	clock_t start_time=clock();
	std::thread thread1(bubblesort,array1,LEN);
	std::thread thread2(bubblesort,array2,LEN);
	thread1.join();
	thread2.join();
	clock_t stop_time=clock();
	std::cout<<1.0*(stop_time-start_time)/CLOCKS_PER_SEC; 
} 

void bubblesort(int* array,int len)
{
	for(int i=1;i<len;i++)
	{
		for(int j=0;j<len-i;j++)
		{
			if (array[j]>array[j+1])
			{
				int temp=array[j];
				array[j]=array[j+1];
				array[j+1]=array[j];
			}
		}
	}
}

单线程:

#include <iostream>
#include <thread>
#include <ctime>
#include <cstdlib>

#define LEN 100000

void bubblesort(int* array,int len);

int main(void)
{
	int* array1;
	int* array2;
	array1=new int[LEN];
	array2=new int[LEN];
	srand(time(0));
	
	for (int i=0;i<LEN;i++)
	{
		array1[i]=(rand()%10000)*(rand()%10000);
		array2[i]=(rand()%10000)*(rand()%10000);
	}
	
	
	clock_t start_time=clock();
	bubblesort(array1,LEN);
	bubblesort(array2,LEN);
	clock_t stop_time=clock();
	std::cout<<1.0*(stop_time-start_time)/CLOCKS_PER_SEC; 
} 

void bubblesort(int* array,int len)
{
	for(int i=1;i<len;i++)
	{
		for(int j=0;j<len-i;j++)
		{
			if (array[j]>array[j+1])
			{
				int temp=array[j];
				array[j]=array[j+1];
				array[j+1]=array[j];
			}
		}
	}
}

五、是否循环等待 detach和join

  • detach方式,启动的线程自主在后台运行,当前的代码继续往下执行,不等待新线程结束。
  • join方式,等待启动的线程完成,才会继续往下执行。

1、直接一个小实验分析join的用法

#include <thread>
#include <iostream>
#include <windows.h>


//func1和func2这两个函数,分别打印对应内容10次 
void func1()
{	
	int count=0;
	while(count<=10)
	{
		std::cout<<"thread1 working"<<std::endl;
		Sleep(1000);
		count++;
	}
}

void func2()
{	
	int count=0;
	while(count<=10)
	{
		std::cout<<"thread2 working"<<std::endl;
		Sleep(1000);
		count++;
	}
}

int main(void)
{
	std::cout<<"one"<<std::endl;
	std::thread thread1(func1);
	std::thread thread2(func2);
	std::cout<<"two"<<std::endl;
	//这里使用了join,程序会一直循环等待 
	thread1.join();
	thread2.join();
	std::cout<<"three";
}

程序的输出结果是:

one
two
thread2 working
thread1 working
thread1 working
thread2 working
thread1 working
thread2 working
thread1 workingthread2 working

thread2 working
thread1 working
thread1 working
thread2 working
thread2 working
thread1 working
thread2 working
thread1 working
thread2 working
thread1 working
thread2 working
thread1 working
thread2 working
thread1 working
three

可以看出,位于join之前的one和two还是可以被打印的,而three要等到thread1和thread2执行完毕,才被打印。可以把join理解为循环等待函数。在thread1和thread2执行完毕前,主程序一直卡在里面不继续执行,知道两个线程结束,主程序才继续执行。

(此外,由于两线程一同占用输出流,可以看到thread1的某个换行符被吞掉了,我想这是两线程占用资源冲突造成的)

2、把join改为detach重复实验

int main(void)
{
	std::cout<<"one"<<std::endl;
	std::thread thread1(func1);
	std::thread thread2(func2);
	std::cout<<"two"<<std::endl;
	//这里使用了detach,主线程继续运行 
	thread1.detach();
	thread2.detach();
	std::cout<<"three";
}

one
two
thread1 working
thread2 working
three

可以看到thread1和thread2中的函数只执行了一次打印,主线程就结束了。那么子线程也结束了。

3、只改一个join呢?

int main(void)
{
	std::cout<<"one"<<std::endl;
	std::thread thread1(func1);
	std::thread thread2(func2);
	std::cout<<"two"<<std::endl;
	//这里使用了join,程序会一直循环等待 
	thread1.join();
	thread2.detach();
	std::cout<<"three";
}

one
two
thread1 working
thread2 working
thread1 workingthread2 working

thread1 workingthread2 working

thread1 workingthread2 working

thread2 working
thread1 working
thread2 working
thread1 working
thread1 working
thread2 working
thread2 working
thread1 working
thread1 workingthread2 working

thread1 working
thread2 working
thread1 working
thread2 working
three

这印证了我们对join的理解,它的的确确就是一个循环等待函数一般的存在(像arduino里和ROS里面一样)

上面这个例子,我们可以说吗,托thread1的福,thread2也得以运行完毕,但是如果thread2运行得慢一点,那么thread2依旧无法执行完毕。

4、继续做一些操作呢?

int main(void)
{
	std::cout<<"one"<<std::endl;
	std::thread thread1(func1);
	std::thread thread2(func2);
	std::cout<<"two"<<std::endl;
	thread1.detach();
	std::cout<<"three"<<std::endl;
	thread2.join();
	std::cout<<"four";
}

我把代码改成了这样,读者可以自行验证输出结果,非常有意思!

参考资料:
多线程的学习_多线程学习-CSDN博客

C++多线程详解(全网最全) - 知乎 (zhihu.com)

 

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

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

相关文章

MS2660:L1 频段卫星导航射频前端低噪声放大器芯片

MS2660 是一款具有高增益、低噪声系数的低噪声放 大器&#xff08;LNA&#xff09;芯片&#xff0c;支持 L1 频段多模式全球卫星定位&#xff0c;可 以应用于 GPS、北斗二代、伽利略、Glonass 等 GNSS 导航 接收机中。芯片采用先进工艺制造&#xff0c;封装采用 2 mm 2 mm …

【latex】参考文献排版前移,在最前面引用\usepackage{url}

【LaTeX】参考文献排版前移&#xff0c;在最前面引用\usepackage{url} 写在最前面完整解决步骤请教申申latex编译报错解决方案 写在最前面 参考文献从21开始排版前移了 解决方案&#xff1a;在最前面加一行 \usepackage{url}完整解决步骤 请教申申 申申yyds&#xff01;&am…

HarmonyOS—构建第一个ArkTS应用(Stage模型)

创建ArkTS工程 构建第一个页面 若首次打开DevEco Studio&#xff0c;请点击Create Project创建工程。如果已经打开了一个工程&#xff0c;请在菜单栏选择File > New > Create Project来创建一个新工程。选择Application应用开发&#xff0c;选择模板“Empty Ability”&am…

推荐几款IDEA插件,助你玩转Mybatis开发

在软件开发的征程中&#xff0c;MyBatis 框架一直是 Java 开发者中的首选&#xff0c;其简洁的 SQL 映射和强大的灵活性使其成为持久层框架的瑰宝。然而&#xff0c;在我们开发过程中&#xff0c;很多人都曾面对过一个相对繁琐的问题&#xff1a;如何高效而准确地将数据库表映射…

element的el-select组件数据过多使用分页

一、代码实现 1. 单个下拉框 <template><div><!-- 为了启用远程搜索&#xff0c;需要将filterable和remote设置为true&#xff0c;同时传入一个remote-method。remote-method为一个Function&#xff0c;它会在输入值发生变化时调用&#xff0c;参数为当前输入…

Ceph应用管理

目录 资源池 Pool 管理 创建 CephFS 文件系统 MDS 接口 服务端操作 客户端操作 创建 Ceph 块存储系统 RBD 接口 创建 Ceph 对象存储系统 RGW 接口 OSD 故障模拟与恢复 资源池 Pool 管理 我们如何向 Ceph 中存储数据呢&#xff1f;首先我们需要在 Ceph 中定义一个 Pool…

代码随想录算法训练营29期|day 21 任务以及具体安排

530.二叉搜索树的最小绝对差 力扣题目链接(opens new window) 给你一棵所有节点为非负值的二叉搜索树&#xff0c;请你计算树中任意两节点的差的绝对值的最小值。 示例&#xff1a; 提示&#xff1a;树中至少有 2 个节点。 class Solution {TreeNode pre;// 记录上一个遍历的…

C++ 数论相关题目(约数)

1、试除法求约数 主要还是可以成对的求约数进行优化&#xff0c;不然会超时。 时间复杂度根号n #include <iostream> #include <vector> #include <algorithm>using namespace std;int n;vector<int> solve(int a) {vector<int> res;for(int i…

力扣714. 买卖股票的最佳时机含手续费(java 动态规划)

Problem: 714. 买卖股票的最佳时机含手续费 文章目录 题目描述思路解题方法复杂度Code 题目描述 思路 1.构建多阶段决策模型&#xff1a;n天对应n个阶段&#xff0c;每个阶段决策&#xff1a;买股票、卖股票、不操作&#xff1b;买股票只有当前不持有股票才行&#xff0c;卖股票…

IntelliJ IDEA 拉取gitlab项目

一、准备好Gitlab服务器及项目 http://192.168.31.104/root/com.saas.swaggerdemogit 二、打开 IntelliJ IDEA安装插件 打开GitLab上的项目&#xff0c;输入项目地址 http://192.168.31.104/root/com.saas.swaggerdemogit 弹出输入登录用户名密码&#xff0c;完成。 操作Comm…

【数据结构】二叉树-堆(top-k问题,堆排序,时间复杂度)

&#x1f308;个人主页&#xff1a;秦jh__https://blog.csdn.net/qinjh_?spm1010.2135.3001.5343&#x1f525; 系列专栏&#xff1a;《数据结构》https://blog.csdn.net/qinjh_/category_12536791.html?spm1001.2014.3001.5482 ​​ 目录 堆排序 第一种 ​编辑 第二种 …

Linux开发工具:yum、vim、gcc/g++、makefile

文章目录 一. Linux软件包管理器-yum使用1. Linux 安装软件的方式2. yum 三板斧yum listyum installyum remove 二. Linux编辑器-vim使用1. vim 的基本概念2. vim 的基本操作3. vim 的命令模式命令集4. vim 底行模式命令集 三. Linux编译器-gcc/g使用1. 快速使用2. 程序翻译过程…

【Linux】文件系统与软硬连接

欢迎来到Cefler的博客&#x1f601; &#x1f54c;博客主页&#xff1a;折纸花满衣 &#x1f3e0;个人专栏&#xff1a;题目解析 &#x1f30e;推荐文章&#xff1a;【LeetCode】winter vacation training 目录 &#x1f449;&#x1f3fb; 磁盘HDD的物理存储结构磁盘的逻辑抽象…

element-ui Vue 封装组件按钮工具栏,使用slot插槽

封装常用按钮工具栏,方便其它页面调用 缺点:工具栏下面div会显示工具栏下面,下面需要使用margin-top:40px(小学生一个没整明白)希望大神能帮解决 运行效果 组件代码 tt-btnBar.vue <template><div><div class"btnbarP"><div class"btnbar…

【C语言】详解文件与文件操作

1.什么是文件 文件是记录在外存上的相关信息的命名组合&#xff0c;但是在程序设计中&#xff0c;我们⼀般谈的文件有两种&#xff1a;程序文件、数据文件&#xff08;从文件功能的角度来分类的&#xff09;。 1.1程序文件 程序文件包括&#xff1a; 源程序文件 &#xff0…

Pytorch基础知识点复习

文章目录 并行计算单卡训练多卡训练单机多卡DP多机多卡DDPDP 与 DDP 的优缺点 PyTorch的主要组成模块Pytorch的主要组成模块包括那些呢&#xff1f;Dataset和DataLoader的作用是什么&#xff0c;我们如何构建自己的Dataset和DataLoader&#xff1f;神经网络的一般构造方法&…

头部姿态估计算法原理

人脸检测 文章目录 人脸检测一、前言二、原理三、代码实现 一、前言 头部姿态估计是通过一幅面部图像来获得头部的姿态角. 在3D空间中&#xff0c;表示物体的旋转可以由三个欧拉角(Euler Angle)来表示&#xff1a;分别计算 pitch(围绕X轴旋转)&#xff0c;yaw(围绕Y轴旋转) 和…

网络新手必读!CentOS 7系统IP配置与防火墙管理攻略

前言 掌握CentOS 7系统的网络配置技能&#xff0c;从IP地址、子网掩码、DNS到防火墙&#xff0c;一篇文章全搞定&#xff01;无论你是网络新手还是寻求深入了解的高手&#xff0c;这篇文章都为你提供了简单易懂的配置指南&#xff0c;助你轻松驾驭系统网络。摆脱繁琐的配置困扰…

C# 图解教程 第5版 —— 第23章 异常

文章目录 23.1 什么是异常23.2 try 语句23.3 异常类23.4 catch 子句23.5 异常过滤器23.6 catch 子句段23.7 finally 块23.8 为异常寻找处理程序23.9 进一步搜索23.9.1 一般法则23.9.2 搜索调用栈的示例&#xff08;*&#xff09; 23.10 抛出异常23.11 不带异常对象的抛出23.12 …

基于变换域的模版匹配

模板匹配原理 图像的空间域与其他域之间的变换&#xff0c;如傅里叶变换&#xff0c;小波变换&#xff0c;轮廓波变换&#xff0c;剪切波变换等&#xff0c;实际上是图像在其他坐标领域中的表现。在空间域中&#xff0c;图像的信息是像素值和坐标位置&#xff1b;在其他域中&a…