深度探究多线程的效率以及多线程的使用建议

news2025/8/7 14:37:52

随着时代发展,电脑的核心数慢慢增多,在开发程序的过程中,是否选择使用多线程这是个比较大的问题,下面我通过一个程序去深入理解多线程对程序速度的影响到底有多大

计算亿级别个数的累加和:

单线程模型运行程序:

首先先生成随机数:

int rand(int *array)
{
	for(long long i=0;i<N;i++)
	{
		array[i] = rand()%100;
	}
}

最简单版本的计算方法,直接在main函数里面去循环调用:

#include<iostream>
#include<time.h>

#define N 500000000
#define THREAD_NUM 15

int rand(int *array)
{
	for(long long i=0;i<N;i++)
	{
		array[i] = rand()%100;
	}
}

int main()
{
	srand((unsigned)time(NULL));  
	int* array = new int[N];
	rand(array);
	clock_t start, finish;  
    double  duration;
    long long sum = 0;
    
    start = clock();
	for (int i = 0; i < N; ++i) {
		sum += array[i];
	}

	finish = clock();  
	duration = (double)(finish - start) / CLOCKS_PER_SEC;  
	printf( "%f seconds\n", duration );

	delete [] array;
	printf("sum:%lld\n", sum);                     
}

求出计算时间:

 统计时间为:0.849000秒

多线程模型拆分计算:

然后就要用到多线程模型来计算,将500000000个数字进行拆分,拆成N份,然后在N的线程中分别求和,最后再累计它们的和:

这里要用到pthread库去完成计算:

首先建立线程池:

	pthread_t threads[THREAD_NUM];

建立结构体向线程中传入参数,这里注意,我们用的利用pthread的pthread_create函数去创建线程

struct Result
{
	int *array;
	long long start;
	long long length;
	long long result;
	int index;
};

array为要传入的数组,start和length为要计算的位置和需要计算的长度,最后result为要传回的值,index代表当前的线程号 

pthread_create函数的第四个参数为向线程函数传入的参数,只能有一个参数,所以我们利用结构体去把四个参数传入到第四个参数内,并且根据线程数去依次创建线程:

	for(int i=0;i<THREAD_NUM;i++)
	{
	 	result[i].array=array; // +i*N/THREAD_NUM;
	 	result[i].start = i * length;
	 	result[i].length = length;
	 	result[i].result = 0;
	 	result[i].index = i;
		pthread_create(&threads[i],NULL,func,(void*)(&result[i]));	                                                
	}           
	

然后建立要执行的单线程函数:

void *func(void *result)
{
	
	if (nullptr == result) {
		return nullptr;
	}
	
	Result* source = static_cast<Result*>(result);
 	const long long end = source->start + source->length;
 	long long sum = 0;
 	const int *array = source->array;
 	
	for(long long i = source->start; i < end; ++i)
	{
		sum += array[i];
	}
	
	source->result = sum;

	return nullptr;
}

整体多线程全程序代码如下:

#include<iostream>
#include<time.h>
#include<pthread.h>

#define N 500000000
#define THREAD_NUM 1

struct Result
{
	int *array;
	long long start;
	long long length;
	long long result;
	int index;
};

int rand(int *array)
{
	for(long long i=0;i<N;i++)
	{
		array[i] = rand()%100;
	}
}

void *func(void *result)
{
	
	if (nullptr == result) {
		return nullptr;
	}
	
	Result* source = static_cast<Result*>(result);
 	const long long end = source->start + source->length;
 	long long sum = 0;
 	const int *array = source->array;
 	
	for(long long i = source->start; i < end; ++i)
	{
		sum += array[i];
	}
	
	source->result = sum;	
	return nullptr;
}

int main()
{
	srand((unsigned)time(NULL));  
	
	
	int* array = new int[N];
	
	rand(array);
	Result result[THREAD_NUM];
	pthread_t threads[THREAD_NUM];
	long long length = N/THREAD_NUM;
			clock_t start, finish;  
    double  duration;
    start = clock();
	for(int i=0;i<THREAD_NUM;i++)
	{
	 	result[i].array=array;
	 	result[i].start = i * length;
	 	result[i].length = length;
	 	result[i].result = 0;
	 	result[i].index = i;
		pthread_create(&threads[i],NULL,func,(void*)(&result[i]));	                                                
	}           
	

	for(long long i=0;i<THREAD_NUM;i++)    
	{
		long long ret = pthread_join(threads[i],NULL);
	}
	

		long long sum = 0;
	for (long long i = 0; i < THREAD_NUM; ++i) {
		sum += result[i].result;
	}
	
	finish = clock();  
	duration = (double)(finish - start) / CLOCKS_PER_SEC;  
	printf( "%f seconds\n", duration );

	delete [] array;
	printf("sum:%lld\n", sum);                     
}

其中宏定义如下:

#define N 500000000
#define THREAD_NUM 1

其中N代表本次要处理的数据, THREAD_NUM代表要拆分的线程数

先运行一个线程的时候,求得运算时间:

时间为1.013000秒 

 将宏定义改为如下:

#define N 500000000
#define THREAD_NUM 2

 将计算拆分成两个线程来求得运行时间:

 可以发现运行时间为0.510000秒,比单线程整整少了一半的时间

接着将计算拆分成四线程:

#define N 500000000
#define THREAD_NUM 4

 运行时间变为了0.294000秒!

接着尝试将计算拆分为了更多线程,然后得到下表:

由于我的电脑CPU为12线程的,所以统计到12线程,可以发现使用了多线程加速计算后,程序效率大大提升

但是当线程突破CPU最大线程限制之后会怎么样呢,我们接着完善这个表:

 可以发现随着线程数量的增大,计算效率反而降低了,这是什么原因造成的呢?

上下文切换:

当每一个线程分配到CPU的一个逻辑处理单元处理的时候,每个线程都能充分计算,但是线程大于逻辑处理单元的时候,基于要有两个线程去共享一个逻辑单元

在线程切换的时候,会保存上一个线程的现场,加载下一个线程的状态,这一过程叫上下文切换,频繁切换这个过程开销不小,所以线程越多,切换越频繁,反而程序效率会更低了

多线程的最大效率:

根据以上数据可以发现:

 当线程数量接近于CPU核心数量的时候,程序的运行效率最大

优化的坑:

在拆分多线程函数的时候,如果处理不好,反而造成的资源浪费更大:

我举个例子:

在同样8线程的条件下:

线程函数这么写:

void *func(void *result)
{
	
	if (nullptr == result) {
		return nullptr;
	}
	
	Result* source = static_cast<Result*>(result);
 	const long long end = source->start + source->length;
 	long long sum = 0;
 	const int *array = source->array;
 	
	for(long long i = source->start; i < end; ++i)
	{
		sum += array[i];
	}
	
	source->result = sum;	
	return nullptr;
}

运行时间如下:

 但是当写成如下格式的时候:

void *func(void *result)
{
	
	if (nullptr == result) {
		return nullptr;
	}
	
	Result* source = static_cast<Result*>(result);
 	const long long end = source->start + source->length;
 	long long sum = 0;
 	const int *array = source->array;
 	
	for(long long i = source->start; i < end; ++i)
	{
		source->result += array[i];
	}
		
	return nullptr;
}

运行时间为:

仅仅把 

	for(long long i = source->start; i < end; ++i)
	{
		sum += array[i];
	}
	
	source->result = sum;	

改为:

	for(long long i = source->start; i < end; ++i)
	{
		source->result += array[i];
	}

运行效率大大降低

可以看前者和后者的汇编代码:

前者

后者:

主要是后者比前者多了几次寻址操作

可见多线程如果使用不当的情况下,还不如使用单线程

完整测试程序:

#include<iostream>
#include<time.h>
#include<pthread.h>

#define N 500000000
#define THREAD_NUM 4

struct Result
{
	int *array;
	long long start;
	long long length;
	long long result;
	int index;
};

int rand(int *array)
{
	for(long long i=0;i<N;i++)
	{
		array[i] = rand()%100;
	}
}

void *func(void *result)
{
	
	if (nullptr == result) {
		return nullptr;
	}
	
	Result* source = static_cast<Result*>(result);
 	const long long end = source->start + source->length;
 	long long sum = 0;
 	const int *array = source->array;
 	
	for(long long i = source->start; i < end; ++i)
	{
		sum += array[i];
		//((Result*)result)->result+=(((Result*)result)->array)[N/THREAD_NUM];
	}
	
	source->result = sum;

	
	return nullptr;
}

int main()
{
	srand((unsigned)time(NULL));  
	
	
	int* array = new int[N];
	
	rand(array);
#if 1
	Result result[THREAD_NUM];
	pthread_t threads[THREAD_NUM];
	long long length = N/THREAD_NUM;
			clock_t start, finish;  
    double  duration;
    start = clock();
	for(int i=0;i<THREAD_NUM;i++)
	{
	 	result[i].array=array; // +i*N/THREAD_NUM;
	 	result[i].start = i * length;
	 	result[i].length = length;
	 	result[i].result = 0;
	 	result[i].index = i;
		pthread_create(&threads[i],NULL,func,(void*)(&result[i]));	                                                
	}           
	

	for(long long i=0;i<THREAD_NUM;i++)    
	{
		//void* resout = nullptr;
		long long ret = pthread_join(threads[i],NULL);
		printf("%d\n",result[i].result);
	}
	

		long long sum = 0;
	for (long long i = 0; i < THREAD_NUM; ++i) {
		sum += result[i].result;
	}
	
	finish = clock();  
	   duration = (double)(finish - start) / CLOCKS_PER_SEC;  
	   printf( "%f seconds\n", duration );
	
#else
	clock_t start, finish;  
    double  duration;
    start = clock();
    
	long long sum = 0;
	for (int i = 0; i < N; ++i) {
		sum += array[i];
	}
	finish = clock();  
	   duration = (double)(finish - start) 
	   printf( "%f seconds\n", duration );
#endif
	delete [] array;
	printf("sum:%lld\n", sum);                     
}

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

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

相关文章

SNMP(二)

♥️作者&#xff1a;小刘在C站 ♥️每天分享云计算网络运维课堂笔记&#xff0c;一起努力&#xff0c;共赴美好人生&#xff01; ♥️夕阳下&#xff0c;是最美的&#xff0c;绽放。 目录 一.SNMP是什么 二.SolarWinds 网管软件使用 步骤1 步骤2 步骤3 步骤4 三.SNMP命令…

使用go pprof进行golang程序内存分析

引言 日常项目&#xff0c;有时会出现oom的情况&#xff0c;这时候我们光依靠code review进行问题定位是很困难的。这里我们需要一个排查工具&#xff0c;来定位是哪里的代码导致内存溢出的&#xff0c;这个工具就是pprof前提 如果是非http(s)服务类的&#xff0c;需要在代码…

基于PHP+MySQL的宠物领养救助社交网站

当前很多的宠物被抛弃和虐杀,它们没有选择权,我们强制性的把狗带进人类的生活中,然后又无情的抛弃,让它们无家可归,变成流浪狗,它们做错了什么&#xff1f;流浪动物被主人遗弃之后居无定所,时刻面对着严寒、酷暑、生病、死亡,饥饿、等一系列威胁它们生命的存在为了能够让这些。…

BlenderGIS插件 城市建筑3D模型自动生成 教程

目录 一、下载Blender和BlenderGIS 二、解决 No imaging library...报错 三、生成城市3D模型 四、导出模型 本文所需文件可在如下链接下载&#xff0c;或者直接按照博文下载步骤下载 https://download.csdn.net/download/ChaoChao66666/87071901?spm1001.2014.3001.550…

Node的Web编程

一、node的事件处理 1、node采用的事件驱动模式来进行事件处理的&#xff1a;只有当事件被触发时才执行相关程序 2、node是单线程运行的&#xff1a;采用事件轮询方式&#xff0c;不断的查询事件队列中的消息&#xff0c;然后根据消息执行对应的回调函数 3、node事件机制中的…

ASEMI代理艾赛斯DSP25-12A,整流二极管DSP25-12A

编辑-Z 艾赛斯整流二极管DSP25-12A参数&#xff1a; 型号&#xff1a;DSP25-12A 最大重复反向阻断电压&#xff08;VRRM&#xff09;&#xff1a;1200V 反向电流、漏极电流&#xff08;IR&#xff09;&#xff1a;40uA 正向电压降&#xff08;VF&#xff09;&#xff1a;1…

this用法总结

文章目录1.常规下this的指向1.1 全局环境中的this1.2 上下文对象调用中的this1.3 this指向绑定事件的元素1.4 箭头函数的this指向2. 改变this指向2.1 call - Function.prototype.call( )2.1.1 call的第一个参数2.1.2 call接受多个参数2.1.3 调用对象的原生方法2.2 apply - Func…

LFMCW雷达测速基础- 多普勒频移和2DFFT

LFMCW雷达测速基础1 基本测速原理——多普勒频移1.0 写在前面1.1 多普勒效应1.2 多普勒频移疑问2 LFMCW雷达测速原理2.1 Doppler Shift 到底是调频还是调相2.2 为什么是二维FFT1 基本测速原理——多普勒频移 1.0 写在前面 振动/波在空间中传播有三个关键变量&#xff0c;分别…

iNFTnews|Web3正在重新定义粉丝的意义

数字身份正在发展&#xff0c;粉丝圈本身也在发展。Web3技术使粉丝们能够以一种更加人际化的方式与同好、媒体、内容创作者甚至他们崇拜的名人进行互动&#xff0c;粉丝和创作者都可以获得更人性化的体验。另外&#xff0c;一些激励措施还有望从根本上改变我们对名人概念本身的…

Java#14(StringJoiner)

目录 一.StringJoiner的构造方法和成员方法 StringJoiner是jdk8才出现的 作用:可以高效和方便的拼接字符串 1.StringJoiner的构造方法 2.StringJoiner的成员方法 一.StringJoiner的构造方法和成员方法 StringJoiner是jdk8才出现的 作用:可以高效和方便的拼接字符串 1.Stri…

一篇文章入门单链表+刷题实践【java实现+详细注释】

文章目录节点定义链表类获取链表长度清空链表添加节点到链表尾部根据id来删除节点根据id来查询节点修改相同id的节点信息打印链表逆序打印反转链表测试代码打印信息习题反转链表删除链表的节点删除链表的倒数第k个节点节点定义 关于节点里面所存储的信息&#xff0c;需要什么就…

洛谷 P1064 - 金明的预算方案(分组背包)

题目描述 金明今天很开心&#xff0c;家里购置的新房就要领钥匙了&#xff0c;新房里有一间金明自己专用的很宽敞的房间。更让他高兴的是&#xff0c;妈妈昨天对他说&#xff1a;“你的房间需要购买哪些物品&#xff0c;怎么布置&#xff0c;你说了算&#xff0c;只要不超过 n…

HTML学生个人网站作业设计:(宠物官网8页)——bootstarp响应式含有轮播图,响应式页面

⛵ 源码获取 文末联系 ✈ Web前端开发技术 描述 网页设计题材&#xff0c;DIVCSS 布局制作,HTMLCSS网页设计期末课程大作业 | 公司官网网站 | 企业官网 | 酒店官网 | 等网站的设计与制 | HTML期末大学生网页设计作业&#xff0c;Web大学生网页 HTML&#xff1a;结构 CSS&#…

H7-TOOL发布V2.19,脱机烧录新增中微半导体、广芯微电子、中移芯昇以及极海和灵动新系列,增加PWM发生器等功能(2022-11-17)

H7-TOOL详细介绍&#xff1a;H7-TOOL开发工具&#xff0c;1拖4/16脱机烧录&#xff0c;高速DAPLINK&#xff0c;RTOS Trace&#xff0c;CAN/串口助手, 示波器, RTT等&#xff0c;支持WiFi&#xff0c;以太网&#xff0c;高速USB和手持 - H7-TOOL开发工具 - 硬汉嵌入式论坛 - Po…

JavaScript对象与内置对象

JavaScript对象与内置对象 文章目录JavaScript对象与内置对象1.JavaScript的基本对象1.1 创建对象1.2 对象的调用1.3 构造函数和对象1.4 变量对象属性2.JavaScript的内置对象2.1 内置对象概念3.JavaScript的Math对象3.1 Math对象概念3.2 Math绝对值和三个取整方法3.3 随机数方法…

AndroidStudio 导入项目模块失败

平台 Ubuntu 20.04AndroidStudio Android Studio Dolphin | 2021.3.1 Patch 1 Build #AI-213.7172.25.2113.9123335, built on September 30, 2022 Runtime version: 11.0.130-b1751.21-8125866 amd64 VM: OpenJDK 64-Bit Server VM by JetBrains s.r.o. Linux 5.15.0-53-gen…

Structure-Aware Transformer for Graph Representation Learning

Structure-Aware Transformer for Graph Representation Learning 在图表示学习中&#xff0c;Transformer结构通过位置编码对图形结构进行编码&#xff0c;克服了GNN的几个局限性&#xff0c;但其使用位置编码生成的节点表示不一定能捕获他们之间的结构相似性&#xff0c;所以…

「Redis数据结构」哈希表(Dict)

「Redis数据结构」哈希表&#xff08;Dict&#xff09; 文章目录「Redis数据结构」哈希表&#xff08;Dict&#xff09;[toc]一、概述二、结构三、哈希冲突四、链式哈希五、rehash六、 渐进式 rehash七、总结参考我们知道Redis是一个键值型&#xff08;Key-Value Pair&#xff…

Python自制图片拼图小游戏

前言 唉&#xff0c;怎么说&#xff0c;感觉只有上班的时候摸鱼玩游戏才是最爽的 等于带薪摸鱼&#xff0c;现在不是有点流行说什么 带薪…带薪** 干嘛的 今天也是有点无聊&#xff0c;就想起之前搞了个拼图的小游戏&#xff0c;可以自己更改照片的 嘿嘿 这不刚玩了一局&am…

【Python】Pyside6简易版教程

文章目录一、安装及常见指令1.1 安装1.2 转换指令二、设计UI2.1 类别2.1.1 Spacer2.1.2 Buttons2.1.3 Input Widgets2.1.4 Display Widgets2.1.5 注意事项2.2 对象和属性2.2.1 对象2.2.2 属性2.2.2.1 属性的层级结构2.2.2.2 重要的属性2.2.2.3 插入图片三、回到Python3.1 给现有…