并发编程中用到的几种常见锁

news2024/11/16 11:44:41

没有加锁而造成的数据竞争

任务:使用10个线程,同时对一个count加100000;最后我们期望的结果是100000;

实验代码

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <time.h>

#define THREAD_COUNT	10

//线程回调函数
void *thread_callback(void *args)
{
	int *pcount = (int *)args;
	int i = 0;

	//依次增加100000
	while(i ++ < 100000)
	{
		(*pcount)++; //无锁	
		usleep(1); //睡眠1微秒
	}

}

int main()
{
	clock_t start, finish;
	void *thread_result;
	pthread_t threadid[THREAD_COUNT] = {0};

	start = clock();

	int i = 0;
	int count = 0;
	for(i = 0; i < THREAD_COUNT; ++i)
	{
		//创建线程
		pthread_create(&threadid[i], NULL, thread_callback, &count);

	}
	
	for(i = 0; i < THREAD_COUNT; ++i) {
		pthread_join(threadid[i], &thread_result);
	}
	finish = clock();
	printf("count: %d\n", count);
	printf("NoLock : %f\n", (double)(finish - start) / CLOCKS_PER_SEC);


	return 0;
}

实验结果

在这里插入图片描述

观察发现,count并没有加到1000000,原因如下:

(*pcount)++可以分解为3条汇编指令

mov [count], eax;
inc eax;
mov eax, [count];

在并发环境下,会出现如下的执行顺序

在这里插入图片描述

如:count = 50, 线程1先执行 mov [count], eax;即eax = 50

然后转到线程2执行三条汇编指令,执行后count为51;

此时再转回去线程1执行剩余两条语句,由于前面eax = 50,经过inc eax之后eax变为51,再经过mov eax, [count],count 变为51,发现两次执行(*pcount)++之后 *pcount只自增了1。

*解决办法:在进行 (pcount)++时进行加锁

互斥锁(mutex)

实现代码如下

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <time.h>

#define THREAD_COUNT	10

pthread_mutex_t mutex;
//线程回调函数
void *thread_callback(void *args)
{
	int *pcount = (int *)args;
	int i = 0;

	//依次增加100000
	while(i ++ < 100000)
	{
		pthread_mutex_lock(&mutex); //加互斥锁
		(*pcount)++;
		pthread_mutex_unlock(&mutex); //解锁
		usleep(1); //睡眠1微秒
	}

}

int main()
{
	clock_t start, finish;
	void *thread_result;
	pthread_t threadid[THREAD_COUNT] = {0};

	start = clock();
    pthread_mutex_init(&mutex, NULL);
	int i = 0;
	int count = 0;
	for(i = 0; i < THREAD_COUNT; ++i)
	{
		//创建线程
		pthread_create(&threadid[i], NULL, thread_callback, &count);

	}
	
	for(i = 0; i < THREAD_COUNT; ++i) {
		pthread_join(threadid[i], &thread_result);
	}
	finish = clock();
	printf("count: %d\n", count);
	printf("mutex : %f\n", (double)(finish - start) / CLOCKS_PER_SEC);
    
	pthread_mutex_destroy(&mutex);
	return 0;
}

实验结果

在这里插入图片描述

可以看出,count最终加到了1000000,解决了数据竞争的问题,但是互斥锁会引起线程切换,适合于锁的内容比较多的场景使用,比如线程安全的rbtree添加节点。

自旋锁(spinlock)

自旋锁不会引起线程切换,不会让出CPU,一直在原地空转。适合于锁的内容较少的情况下使用。

代码如下

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <time.h>

#define THREAD_COUNT	10

pthread_spinlock_t spinlock;
//线程回调函数
void *thread_callback(void *args)
{
	int *pcount = (int *)args;
	int i = 0;

	//依次增加100000
	while(i ++ < 100000)
	{
		pthread_spin_lock(&spinlock);
		(*pcount)++;
		pthread_spin_unlock(&spinlock);
		//usleep(1); //睡眠1微秒
	}

}

int main()
{
	clock_t start, finish;
	void *thread_result;
	pthread_t threadid[THREAD_COUNT] = {0};

	start = clock();
    pthread_spin_init(&mutex, NULL);
	int i = 0;
	int count = 0;
	for(i = 0; i < THREAD_COUNT; ++i)
	{
		//创建线程
		pthread_create(&threadid[i], NULL, thread_callback, &count);

	}
	
	for(i = 0; i < THREAD_COUNT; ++i) {
		pthread_join(threadid[i], &thread_result);
	}
	finish = clock();
	printf("count: %d\n", count);
	printf("mutex : %f\n", (double)(finish - start) / CLOCKS_PER_SEC);
    
	pthread_spin_destroy(&mutex);
	return 0;
}

实验结果

在这里插入图片描述

可以看出也能解决数据竞争问题,但是时间效率低。

原子操作

原子操作就是通过汇编实现单条CPU指令实现(*pcount++)操作

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <time.h>

#define THREAD_COUNT	10

//将三条汇编合成一个原子操作
int inc(int *value, int add)
{
	int old;
	__asm__ volatile(
		"lock; xaddl %2, %1;" //%1 = %2 + %1
		: "=a" (old)
		: "m" (*value), "a"(add) //%1为 *value, %2为add
		: "cc", "memory"
	);
	return old;

}
//线程回调函数
void *thread_callback(void *args)
{
	int *pcount = (int *)args;
	int i = 0;

	//依次增加100000
	while(i ++ < 100000)
	{
		//封装成原子操作
		inc(pcount, 1); 
	}

}

int main()
{
	clock_t start, finish;
	void *thread_result;
	pthread_t threadid[THREAD_COUNT] = {0};

	start = clock();

	int i = 0;
	int count = 0;
	for(i = 0; i < THREAD_COUNT; ++i)
	{
		//创建线程
		pthread_create(&threadid[i], NULL, thread_callback, &count);

	}
	
	for(i = 0; i < THREAD_COUNT; ++i) {
		pthread_join(threadid[i], &thread_result);
	}
	finish = clock();
	printf("count: %d\n", count);
	printf("atomic : %f\n", (double)(finish - start) / CLOCKS_PER_SEC);
	

	return 0;
}

实验结果

在这里插入图片描述

可以看出原子操作的时间开销相比于互斥锁以及自旋锁是最低的。

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

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

相关文章

git项目 拉项目 提交 上传 保姆级教程

git 项目拉取提交 下载git https://git-scm.com/ 拉取代码 打开需要存代码的位置 右键 git bash打开git客户端 输入命令 git clone [复制的地址]上传代码 修改代码 方法一&#xff1a;命令行 打开对应的文件夹&#xff0c;右键打开git bash 拉取最新代码&#xff08;选…

React学习07-React扩展知识

setState setState更新状态的2种写法: setState(stateChange, [callback])------对象式的setState stateChange为状态改变对象(该对象可以体现出状态的更改)callback是可选的回调函数, 它在状态更新完毕、界面也更新后(render调用后)才被调用 setState(updater, [callback])-…

代码随想录算法训练营第九天(字符串)| 28. 实现 strStr(),459.重复的子字符串

代码随想录算法训练营第九天&#xff08;字符串&#xff09;| 28. 实现 strStr()&#xff0c;459.重复的子字符串 28. 实现 strStr() 因为KMP算法很难&#xff0c;大家别奢求 一次就把kmp全理解了&#xff0c;大家刚学KMP一定会有各种各样的疑问&#xff0c;先留着&#xff0…

Qt编写雷达模拟仿真工具2-自定义QGraphicsItem按钮

一、前言 雷达模拟仿真工具&#xff0c;整体结构采用的QGraphicsView框架&#xff0c;场景需要设计一个可点击的自定义按钮出来&#xff0c;在QGraphicsView中一切基础元素点都是基于QGraphicsItem&#xff0c;在QGraphicsItem类中我们可以看到它不继承自QObject&#xff0c;那…

25万美金奖励章鱼加速器2022冬季获胜团队!

全长 1427 字&#xff0c;预计阅读 6 分钟 作者&#xff1a;MiX 2022年12月21日&#xff0c;章鱼加速器2022年冬季 Web3 创业营圆满落幕&#xff0c;61个入营项目中有5个脱颖而出&#xff0c;获得「章鱼未来之星」称号&#xff0c;排名不分先后&#xff0c;他们分别是&#xf…

第五章. 可视化数据分析图表—Seaborn图表(折线图,直方图,条形图,散点图)

第五章. 可视化数据分析图 5.7 Seaborn图表 Seaborn是一个基于Matplotlib的高级可视化效果库&#xff0c;偏向于统计图表&#xff0c;主要针对的是数据挖掘和机器学习中的变量特征选取&#xff0c;相比Matplotlib&#xff0c;他的语法相对简单&#xff0c;但是具有一定的局限性…

Windows下安装oracle19c

oracle 19c 不支持 Windows 7 和 Windows 8 以及 Windows Server 2008&#xff08;GetOverlappedResultEx function 函数不支持 win7&#xff09;&#xff0c;支持 Windows 8.1 以上及 Windows Server 2012 1.下载地址&#xff1a; https://www.oracle.com/database/technolo…

Gnoppix Linux 22.12 发布

导读基于 Kali Linux 的 Linux 滚动发行版 Gnoppix 22.12 带来了 GNOME 43、Linux 内核 6.0 和新的升级。作为传统的现场 CD 发行版 Knoppix 项目的继承者&#xff0c;Gnoppix Linux 是专门为渗透测试和反向工程而设计的。它为网页应用安全和数字权利保护进行了优化。除了对安全…

DNA甲基化重编程为红梨中光诱导的花青素生物合成提供了见解

期刊&#xff1a;Plant Science 影响因子&#xff1a;5.363 发表时间&#xff1a;2022 样本类型&#xff1a;果皮 客户单位&#xff1a;南京农业大学 凌恩生物客户南京农业大学吴俊团队发表在《Plant Science》上的文章“DNA methylatio…

【Ctfer训练计划】——(七)

作者名&#xff1a;Demo不是emo 主页面链接&#xff1a;主页传送门 创作初心&#xff1a;舞台再大&#xff0c;你不上台&#xff0c;永远是观众&#xff0c;没人会关心你努不努力&#xff0c;摔的痛不痛&#xff0c;他们只会看你最后站在什么位置&#xff0c;然后羡慕或鄙夷座…

【数据结构】LinkedList与链表

作者&#xff1a;✿✿ xxxflower. ✿✿ 博客主页&#xff1a;xxxflower的博客 专栏&#xff1a;【数据结构】篇 语录&#xff1a;⭐每一个不曾起舞的日子&#xff0c;都是对生命的辜负。⭐ 文章目录✿1.ArrayList的缺陷✿2.链表2.1链表的概念及结构2.2链表的模拟实现MySingleLi…

aws eks 集群container runtime升级容器管理工具的切换

参考资料 https://cloud-atlas.readthedocs.io/zh_CN/latest/kubernetes/debug/crictl.htmlhttps://zhuanlan.zhihu.com/p/562014518 container runtime Low-Level和High-Level容器运行时。runc、lxc、lmctfy、Docker&#xff08;容器&#xff09;、rkt、cri-o。每一个都是为…

springboot simple (12) springboot RabbitMQ

这里首先简单的介绍了RabbitMQ &#xff0c;然后实现了springboot集成RabbitMQ &#xff0c;包含两个工程&#xff1a; 1 Producer 生产者 2 Consumer 消费者 1 RabbitMQ 简介 AMQP &#xff1a;Advanced Message Queue&#xff0c;高级消息队列协议。 RabbitMQ 是一个由 Erl…

智能大屏兴起,酷开科技赋能营销战略!

随着科技的发展&#xff0c;智能大屏的功能与技术都在日新月异的快速更迭&#xff0c;年轻消费群体也对大尺寸智能大屏表现出了特别的偏爱&#xff0c;以前说到看视频、网上购物、阅读书籍时&#xff0c;人们第一时间就会想到手机&#xff0c;但随着智能大屏的出现&#xff0c;…

商用设计素材库,设计师必备。

免费、商用设计素材网站。 1、菜鸟图库 https://www.sucai999.com/?vNTYxMjky站内平面海报、UI设计、电商淘宝、免抠、高清图片、样机模板等素材非常齐全。还有在线抠图、CDR版本转换功能&#xff0c;能有效的为设计师节省找素材时间&#xff0c;提高工作效率。网站素材都能免…

LeetCode链表经典题目(二)

1. LeetCode203. 移除链表元素 2. LeetCode707.设计链表 3. LeetCode206. 反转链表 4. LeetCode24. 两两交换链表中的节点​ 5. LeetCode19. 删除链表的倒数第 N 个结点 6. LeetCode面试题 02.07. 链表相交 7. LeetCode142. 环形链表 II​ 1. LeetCode203. 移除链表元素 …

基于同豪的道路桥梁设计方案如何实现数字化交付(web发布)

0序&#xff1a; 当下有部分设计人员除了使用Revit、Microstation之外也使用过程的同豪软件进行道路桥梁的BIM建模。如何对同豪的BIM模型进行数字化交付呢&#xff1f; 1数据导出为FBX 具体导出过程可以参考&#xff1a; 数据的导出只要按步骤导出就行&#xff0c;非常简单…

2023最让人期待的日历!每个IT技术人必备

一转眼&#xff0c;2022年已经要接近尾声了..... 这一年的你过的怎么样呢&#xff1f;相比上一年的自己有什么变化呢&#xff1f; 凡是过往皆为序章&#xff0c;所有将来皆为可盼&#xff01;在2023年即将到来之际给大家推荐一本非常适合IT技术人的日历&#x1f449;「小滴日历…

python—you-get下载工具、wget下载工具

目录 wget you-get wget wget工具下载&#xff1a;GNU Wget 1.21.3 for Windows wget工具见&#xff1a;python-外部程序的调用os库、subprocess 模块_觅远的博客-CSDN博客_python外部库调用调出 you-get you-get是一款实用的网站视频下载工具。可以很轻松的下载到网络上…

极限学习机ELM回归预测(MATLAB代码实现)

&#x1f352;&#x1f352;&#x1f352;欢迎关注&#x1f308;&#x1f308;&#x1f308; &#x1f4dd;个人主页&#xff1a;我爱Matlab &#x1f44d;点赞➕评论➕收藏 养成习惯&#xff08;一键三连&#xff09;&#x1f33b;&#x1f33b;&#x1f33b; &#x1f34c;希…