C 语言的 互斥锁、自旋锁、原子操作

news2024/12/23 18:16:40

今天不整 GO 语言,我们来分享一下以前写的 C 代码,来看看 互斥锁,自旋锁和原子操作的 demo

互斥锁

临界区资源已经被1个线程占用,另一个线程过来访问临界资源的时候,会被CPU切换线程,不让运行后来的这个线程

适用于 锁住的内容多(例如红黑数的增加节点操作),切换线程的代价小于等待的代价

自旋锁

临界区资源已经被1个线程占用,另一个线程过来访问临界资源的时候,相当于是一个 while(1)

不断的查看这个资源是否可用,如果可用,就进去访问临界资源,如果不可用,则继续循环访问

适用于锁住的内容少,(例如就执行++操作),切换线程的代价大于等待的代价

原子操作

执行的操作完全不可分割,要么全部成功,要么全部失败

最好的方式就是适用原子操作

实操

需求场景:

1、用10个线程分别对 count 加 100000 次, 看看结果是否是 10*100000

  • main 函数中创建 10 个线程
  • 线程函数中调用 inc 做数据的增加
  • 分别使用 互斥锁,自旋锁,和原子操作,来进行控制

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

#define PTHREAD_NUM	10
#define INFO	printf


pthread_mutex_t mutex;
pthread_spinlock_t spin;


int inc(int *v,int add)
{
	int old;
    //汇编,做一个原子操作
	__asm__ volatile(
		"lock;xaddl %2, %1;"
		:"=a" (old)
		:"m"(*v),"a"(add)
		:"cc","memory"
	);
	
	return old;
}

void * thread_callback(void *arg)
{
	int *count = (int *)arg;

	int i = 100000;
	
while(i--)
	{
	#if 0
//互斥锁
		pthread_mutex_lock(&mutex);
		(*count)++;
		pthread_mutex_unlock(&mutex);
	#elif 0
//自旋锁
		pthread_spin_lock(&spin);
		(*count)++;
		pthread_spin_unlock(&spin);
	#else
//原子操作
		inc(count,1);
	
	#endif
		usleep(1);
	}

}

int main()
{
	pthread_t thread[PTHREAD_NUM] = {0};
	pthread_mutex_init(&mutex,NULL);
	pthread_spin_init(&spin,0);
	
	int count  = 0;

	for(int i = 0;i<PTHREAD_NUM;i++){
		pthread_create(&thread[i],NULL,thread_callback,&count);
	}

	for(int i = 0;i<100;i++)
	{
		INFO("count == %d\n",count);
		sleep(1);
	}
		
	
	return 0;
}

如上代码还是很简单的,感兴趣的 xdm 可以自行运行,控制自己使用互斥锁,自旋锁或者是原子操作看看效果进行对比一下

2、mutex、lock、atomic 性能对比

思路还是和上面的思路类型,咱们可以通过下面的代码来实际初步看看 mutex、lock、atomic 各自的性能

//并发
//互斥锁mutex
//	如果获取不到资源会让出cpu
//	使用场景
//		共享区域执行的内容较多的情况
//自旋锁spinlock
//	如果获取不到资源,会原地自旋,忙等
//	使用场景
//		共享区域执行的内容较少的情况
//原子操作
//	不可分割
//	使用场景
//		做简单++、--操作
//


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

#define MAX_PTHREAD 2
#define LOOP_LEN    1000000000
#define LOOP_ADD    10000

int count = 0;

pthread_mutex_t mutex;
pthread_spinlock_t spin;

typedef void *(*functhread)(void *arg);

void do_add(int num)
{
	int sum = 0;
	for(int i = 0;i<num;i++)
	{
		sum +=i;
	}
}

int atomic_add(int *v,int add)
{
	int old;

	__asm__ volatile(
		"lock;xaddl %2, %1;"
		:"=a" (old)
		:"m"(*v),"a"(add)
		:"cc","memory"
	);
	
	return old;
}

void * atomicthread(void *arg)
{

	for(int i  = 0;i<LOOP_LEN;i++){
		atomic_add(&count,1);
	}
}


void * spinthread(void *arg)
{
	for(int i  = 0;i<LOOP_LEN;i++){

		pthread_spin_lock(&spin);
		count++;
		//do_add(LOOP_ADD);
		pthread_spin_unlock(&spin);

	}
}

void * mutexthread(void *arg)
{
	for(int i  = 0;i<LOOP_LEN;i++){

		pthread_mutex_lock(&mutex);
		count++;

		//do_add(LOOP_ADD);
		pthread_mutex_unlock(&mutex);

	}
}

int test_lock(functhread thre,void * arg)
{

	clock_t start = clock();
	pthread_t tid[MAX_PTHREAD] = {0};

	for(int i = 0;i<MAX_PTHREAD;i++)
	{
	//创建线程
		int ret = pthread_create(&tid[i],NULL,thre,NULL);
		if(0 != ret)
		{
			printf("pthread create rror\n");
			return -1;
		}
	}

	for(int i = 0;i<MAX_PTHREAD;i++){
//回收线程
		pthread_join(tid[i],NULL);
	}

	clock_t end = clock();

	//printf("start  -- %ld\n",start);
	//printf("end  -- %ld\n",end);
	//printf("CLOCKS_PER_SEC  -- %ld\n",CLOCKS_PER_SEC);
	printf("spec lock is  -- %ld\n",(end - start)/CLOCKS_PER_SEC);

}


int main()
{
	pthread_mutex_init(&mutex,NULL);
	pthread_spin_init(&spin,0);
//测试spin
	count = 0;
	printf("use spin ------ \n");
	test_lock(spinthread,NULL);
	printf("count == %d\n",count);


//测试mutex
	count = 0;
	printf("use mutex ------ \n");
	test_lock(mutexthread,NULL);
	printf("count == %d\n",count);

//测试atomic
	count = 0;
	printf("use automic ------ \n");
	test_lock(atomicthread,NULL);
	printf("count == %d\n",count);

	return 0;
}



结果

通过上述结果,我们可以看到,加互斥锁,自旋锁,原子操作,数据都能如我所愿的累加正确,在时间上面他们还是有一定的差异:

自旋锁 和 互斥锁 在此处的案例性能差不多,但是原子操作相对就快了很多

欢迎点赞,关注,收藏

朋友们,你的支持和鼓励,是我坚持分享,提高质量的动力

好了,本次就到这里

技术是开放的,我们的心态,更应是开放的。拥抱变化,向阳而生,努力向前行。

我是阿兵云原生,欢迎点赞关注收藏,下次见~

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

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

相关文章

基础算法(二)——归并排序

归并排序 介绍 归并排序是一种复杂度O(nlog(n)nlog(n)nlog(n))的排序算法&#xff0c;并且在任何情况下都是&#xff0c;但是它不是原地算法&#xff0c;即需要额外存储空间 其原理是&#xff0c;先将区间均匀分成左右两半&#xff0c;然后再对左右两半继续二分&#xff0c;…

Large Language Models Are Reasoning Teachers

Paper name Large Language Models Are Reasoning Teachers Paper Reading Note URL: https://arxiv.org/pdf/2212.10071.pdf twitter 宣传&#xff1a; https://twitter.com/itsnamgyu/status/1605516353439354880 TL;DR 提出了 Fine-tune-CoT 方法&#xff0c;旨在利用非…

Java --- JVM对象内存布局与访问定位

目录 一、对象内存布局 1.1、对象头(Header) 1.1.1、运行时元数据(Mark Word) 1.1.2、 类型指针 1.2、实例数据(Instance Date) 1.3、对齐填充(Padding) 二、对象访问定位 一、对象内存布局 1.1、对象头(Header) 1.1.1、运行时元数据(Mark Word) 1、哈希值(HashCode) 2、G…

【高级人工智能】国科大《高级人工智能》符号主义笔记

国科大《高级人工智能》罗老师部分——符号主义笔记 罗老师上课很有意思&#xff0c;但是这部分内容还是挺难理解的&#xff0c;需要仔细思考今年考试题目这部分跟往年不一样&#xff0c;老师讲的重点&#xff08;A搜索归结原理&#xff09;也没考&#x1f605; 文章目录几个概…

FFmpeg

介绍 FFmpeg是一套可以用来记录、转换数字音频、视频&#xff0c;并能将其转化为流的开源计算机程序。采用LGPL或GPL许可证。它提供了录制、转换以及流化音视频的完整解决方案。它包含了非常先进的音频/视频编解码库libavcodec&#xff0c;为了保证高可移植性和编解码质量&…

Windows 下安装共享文件夹(一步一步带你创建,亲测有效果)

目录1 需求2 实现1 需求 我们想要在自己的Windows电脑上面安装一个共享文件夹&#xff0c;想让其他电脑连我们的共享文件夹&#xff0c;或者我们需要使用代码&#xff0c;连接这个共享文件夹&#xff0c;所以我们必须先在Windows电脑创建一个共享文件夹 2 实现 首先我们创建…

centos7.9安装harbor-offline-installer-v2.4.1.tgz

1.首先下载安装包 可以去github上搜索下载&#xff1a; GitHub: Let’s build from here GitHub 然后选择版本进行下载&#xff0c;一般从githut上下载速度很慢,我已提前下载好&#xff0c;从csdn上下载会比较快&#xff1a; harbor-offline-installer-v2.4.1.tgz: https:…

聊聊跳表?

什么是跳表 跳表&#xff08;Skip List&#xff09;是一种类似于链表的数据结构&#xff0c;其查询、插入、删除的时间复杂度都是O(logn)。 在传统的单链表结构中&#xff0c;查找某个元素需要从链表的头部按顺序遍历&#xff0c;直到找到目标元素为止&#xff0c;查找的时间复…

PowerShell木马免杀利器: Invoke-Obfuscation(过火绒)

Invoke-Obfuscation 简介 Invoke-Obfuscation工具下载地址: https://github.com/danielbohannon/Invoke-Obfuscation Invoke-Obfuscation是一款PowerShell混淆工具&#xff0c;可以将PowerShell脚本加密&#xff0c;使得它的检测和分析变得更加困难。该工具包含多种加密方法&…

HTML实现右下角闪烁弹窗

演示 完整HTML <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document</title> </head&…

【系列01】java运算符及运算符优先级[附带目录 按需服用]

运算符、三元运算符、位运算符、拓展赋值、运算优先级、自增自减 运算符 java代码优先级多用括号**,多用括号()**不仅方便而且增加可读性 自增自减 a 是先赋值再增加a 是先增加再赋值上面都表示 a a1;自减同理由 public class Demo05 {public static void main(String[] ar…

CSS3 之属性

文章目录calcborderbox-shadowbackground-image垂直渐变水平渐变左上角渐变渐变方向写法组合background-clipbackground-attachmentword-wrap:break-word&#xff1a;允许长单词换行到下一行;word-wrap:word-break&#xff1a;text-shadow3、字体white-space 设置如何处理元素内…

Java设计模式中建造者模式是啥/建造者模式实现细节是什么/工厂模式,抽象工厂与建造者模式之间区别是啥

继续整理记录这段时间来的收获&#xff0c;详细代码可在我的Gitee仓库SpringBoot克隆下载学习使用&#xff01; 4.6 建造者模式 4.6.1 概述 分离部件构造(由Builder来创建)和装配(Director组装)实现构建与装配解耦用户只需指定复杂对象类型就可得到最终对象&#xff0c;不需…

医药行业应用APS生产排产软件的必要性

高级计划与排程APS生产排产软件(advanced planning and scheduling) 是一个立足于整个企业生产活动的计算机系统。利用APS生产排产软件的管理手段和信息&#xff0c;企业可优化其从制定生产产量到管理最终产品的整个生产流程中的各个环节。利用实时、准确的数据&#xff0c;APS…

umi4 多环境配置改变

umi3 多环境我们可以根据脚本配置环境变量&#xff0c;然后配置不同的umirc 或者config 文件&#xff0c;从而达到配置多环境的效果。 问题描述 umi4 发布了&#xff0c;还是按照umi3 的配置了多环境&#xff0c; package.json 配置 了不同环境运行和编译脚本&#xff1a; co…

Netty进阶——粘包与半包(固定长度方式解决粘包问题)

目录一、固定长度方式解决粘包问题&#xff08;代码示例&#xff09;1.1、固定长度方式解决粘包问题的服务端代码示例1.2、固定长度方式解决粘包问题的客户端代码示例1.3、分别启动服务端&#xff0c;客户端&#xff0c;查看服务端结果输出一、固定长度方式解决粘包问题&#x…

一零四六、Spark性能调优

本文分为四个部分&#xff0c;基本涵盖了所有Spark优化的点&#xff0c;面试和实际工作中必备。 《Spark性能优化&#xff1a;开发调优篇》《Spark性能优化&#xff1a;资源调优篇》《Spark性能优化&#xff1a;数据倾斜调优篇》《Spark性能优化&#xff1a;shuffle调优篇》Spa…

如何在C++ 中调用 Python

在一些场景下&#xff0c;C 通过调用脚本语言实现一些功能会比用C实现更加的方便。 这里要介绍的是pybind11&#xff0c;pybind11 借鉴了“前辈”Boost.Python&#xff0c;能够在 C 和 Python 之间自由转换&#xff0c;任意翻译两者的语言要素&#xff0c;比如把 C 的 vector …

0-1背包 完全背包 (模板)

目录 【模板】01背包_牛客题霸_牛客网 描述 输入描述&#xff1a; 输出描述&#xff1a; 【模板】完全背包_牛客题霸_牛客网 描述 输入描述&#xff1a; 输出描述&#xff1a; 【模板】01背包_牛客题霸_牛客网 描述 你有一个背包&#xff0c;最多能容纳的体积…

使用语义分割架构的文档扫描仪 DeepLabV3

0 介绍 地址:https://learnopencv.com/deep-learning-based-document-segmentation-using-semantic-segmentation-deeplabv3-on-custom-dataset/ 文档扫描是一个背景分割问题,可以使用多种方法解决。 它是计算机视觉广泛使用的应用程序之一。 在本文中,我们将文档扫描视为语…