锁与原子操作

news2024/10/5 16:22:25

锁与原子操作

以自增操作为例子:

void *func(void *arg) {
	int *pcount = (int *)arg;
	int i = 0;
	//
	while (i ++ < 100000) {
		(*pcount) ++;    // 并不会到达100000
		usleep(1);
	}
}

int main(){
    int i = 0;
	for (i = 0;i < THREAD_COUNT;i ++) {
		pthread_create(&thid[i], NULL, func, &count);
	}
	

	for (i = 0;i < 100;i ++) {
		printf("count --> %d\n", count);
		sleep(1);
	}
}

如果有两个线程对i=20进行自增:idx++

正常是这样:

20230205222319

从内存加载到寄存器 =》 寄存器自增 =》 从寄存器加载到内存

但可能是:

20230205222642

在线程1切换2时,没问题,在线程2切换线程1时,线程1栈中保存的寄存器的值INC是20,写入内存就是21,而不是22.

如果开启O3优化,编译器会将自增转换为原子操作(后面再将)

那我们在编码层怎么做:

  • 加锁(对临界资源是否需要加锁,看它在汇编层面是否是原子操作 )
  • 直接把操作变成原子操作

互斥锁、自旋锁、原子操作

  1. 锁,就是对临界资源加锁,这里加一把互斥锁
void *func(void *arg) {
	int *pcount = (int *)arg;
	int i = 0;
	while (i ++ < 100000) {
        pthread_mutex_lock(&mutex);  
		(*pcount) ++;
		pthread_mutex_unlock(&mutex);
		usleep(1);
	}
}

但其实,这里更应该用自旋锁

  1. 自旋锁:和互斥锁使用一样的,互斥锁在哪用,自旋锁就在哪里用(不让出cpu,一直等着锁被释放)

使用场景:

  • 临界资源复杂的,有系统调用的操作用互斥锁,简单的用自旋(因为等的时间短,消耗的资源还少于线程切换的资源),
  • 有系统调用的就别用自旋了,文件的读写,只允许一个线程访问的,用互斥锁,自增操作,队列读取可以用自旋锁
  • 操作简单,且cpu提供了指令集的,用原子操作
  1. 原子操作

把三条指令(读、自增、写)变成一条:xaddl

原子操作需要cpu指令集的支持,比如这里的xaddl

int inc(int *value, int add) {

	int old;
	__asm__ volatile (
        // xaddl 第2个参数加第1个参数并把值存储到第一个参数;lock,锁cpu操作内存的总线
        // 锁总线、锁缓存的平时也用不到,这里不赘述了
		"lock; xaddl %2, %1;"   
		: "=a" (old)    // old:第0个参数
		: "m" (*value), "a" (add)    // value第一个参数,add是第二个参数
		: "cc", "memory"
	);
	return old;
}

void *func(void *arg) {
	int *pcount = (int *)arg;
	int i = 0;
	while (i ++ < 100000) {
		inc(pcount, 1);   // 原子操作
		usleep(1);
	}
}

而cas是原子操作的一种,也就是cpu指令集中的一个指令:compare and swap,先有比较再有赋值

if (a==b){   // compare
    a=c;      // swap
}

这个就是cmpxchg(a,b,c),用在单例模式中:

if (instance == null){
    instance = malloc(sizeof(object));
}

原子操作记住常用的就行 自增,自减,加减乘除,cas

cpu亲缘性:
在Linux内核中,都是通过task_struct进行调度的,为了避免一个task_struct被切换到其他核(减少系统调用),可以使用系统函数sched_setaffinity() 将一个或多个task_struct绑定到特定的核上

tip1:如果fork指定数量的子进程:

比如说我要创建6个子进程,如果fork()3次,那就是8个了(2,4,8),可以通过以下方式创建:

    int i = 0;
    int num = sysconf(_SC_NPROCESSORS_CONF);  // 获取cpu核心数量
	pid_t pid = 0;
	for (i = 0;i < num/2;i ++) {
		pid = fork();
		if (pid <= (pid_t)0) {    // 如果是子线程(=0),就退出
			break;
		}
	}

tip2:以下内存我没深入理解,放在这里以后补充

线程私有空间:pthrerad_key

线程是进程的一个实体(),是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源.

线程所独享的资源有:程序计数器、寄存器、栈、状态字,共享堆内存

为什么线程要有私有数据:
比如不同线程监听不同的端口,端口的数据,就属于线程的私有空间,不应该被其他线程读取,而线程的私有数据是通过一个Key结构体来实现的

setjmp/longjmp:函数间的跳转,实现try-catch的核心,要实现try-catch,还需线程私用空间

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

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

相关文章

2023年,云计算还有发展前景吗?

云计算在促进经济回暖中扮演者不可或缺的角色&#xff0c;疫情期间复工复产都是基于云计算的基础设施&#xff0c;实现远程办公、在线学习、在线看病、在线政务等等。同时由于数字技术在各个领域的渗透和发展&#xff0c;社会整体对于云技术人才、云服务、算力服务等的需求都在…

虹科分享 | 作为域名系统的SPoF

“SPoF”或“单点故障”背后的思想是&#xff0c;如果系统的一部分发生故障&#xff0c;那么整个系统也会发生故障。 这是不可取的。在IT和安全领域&#xff0c;如果一个组件或子组件的故障会导致系统或应用程序严重中断或降级&#xff0c;那么我们通常认为设计有缺陷。 这就…

OpenAI GPT3 + Flask 利用 text-davinci-003 API 制作自己的交互网页教程 | 附源码 和 Github链接

1. OpenAI GPT3 text-davinci-003 API 最近ChatGPT很火&#xff0c;使用与InstructGPT相同的方法&#xff0c;使用来自人类反馈的强化学习 Reinforcement Learning from Human Feedback (RLHF) 来训练该模型&#xff0c;但数据收集设置略有不同。ChatGPT是在 GPT-3.5 系列中的…

JavaWEB-Servlet

目录 Servlet简介Servlet快速入门Servlet配置详解ServletContext 1 Servlet简介 Servlet 运行在服务端的Java小程序&#xff0c;是sun公司提供一套规范&#xff08;接口&#xff09;&#xff0c;用来处理客户端请求、响应给浏览器的动态资源。但servlet的实质就是java代码&a…

101-并发编程详解(上篇)

并发编程详解在学习之前&#xff0c;如果多线程的理解足够&#xff0c;可以往下学习&#xff0c;否则的话&#xff0c;建议先看看26章博客&#xff08;只是建议&#xff09;&#xff0c;注意&#xff1a;可能有些字的字体不对&#xff0c;那么一般是复制粘贴来的&#xff0c;但…

前端构建工具 Vite

文章目录参考环境构建工具构建工具的主要功能目前主流的前端构建工具Vite为什么使用 Vite冷启动WebpackVite热更新优化热更新优化预构建依赖Webpack VS ViteVite 的缺点首屏性能懒加载与 Vite 相关的基本操作获取create-vite创建项目Project nameSelect a frameworkSelect a va…

信息系统与信息化

1.1 信息系统与信息化 1.1.1 信息的基本概念 信息质量属性(掌握)信息传输模型 1.1.2 信息系统的基本概念1.1.3 信息化的基本概念 信息化的五个层次信息化基本内涵信息化的基本概念&#xff08;了解&#xff09;六要素关系图&#xff08;掌握&#xff09; 1.1.4 信息系统生命周…

Qml学习——动态加载控件

最近在学习Qml&#xff0c;但对Qml的各种用法都不太熟悉&#xff0c;总是会搞忘&#xff0c;所以写几篇文章对学习过程中的遇到的东西做一个记录。 学习参考视频&#xff1a;https://www.bilibili.com/video/BV1Ay4y1W7xd?p1&vd_source0b527ff208c63f0b1150450fd7023fd8 目…

91.【SpringBoot-03】

SpringBoot-03(十四)、任务1.异步任务2.邮件任务(1).简单邮箱发送(2).复杂邮箱发送3.定时任务(1).cron表达式(2).特殊表达式(3).定时任务测试(4).常用cron表达式(十五)、Dubbo和Zookeeper集成1.分布式原理(1).Dubbo文档2.什么是RPC?3.Dubbo的概念和介绍(1).Dubbo是什么(2). Du…

详细聊聊spring核心思想

犹记我当年初学 Spring 时&#xff0c;还需写一个个 XML 文件&#xff0c;当时心里不知所以然&#xff0c;跟着网上的步骤一个一个配置下来&#xff0c;配错一个看着 error 懵半天&#xff0c;不知所谓地瞎改到最后能跑就行&#xff0c;暗自感叹 tmd 这玩意真复杂。 到后来用上…

最短路之Dijkstra(15张图解)

&#x1f33c;多年后再见你 - 乔洋/周林枫 - 单曲 - 网易云音乐 闲来无事听听歌 Dijkstra可解决“单源最短路径”问题 四种最短路算法 Floyd算法 时间复杂度高&#xff0c;但实现容易&#xff08;5行核心代码&#xff09;&#xff0c;可解决负权边&#xff0c;适用于数据范围…

凭借这份《2022测试八股文》候选者逆袭面试官,offer拿到手软

《2023测试面试八股文》800 道软件测试面试真题&#xff0c;高清打印版打包带走&#xff0c;横扫软件测试面试高频问题&#xff0c;涵盖测试理论、Linux、MySQL、Web 测试、接口测试、App 测试、Python、Selenium、性能测试、LordRunner、计算机网络、数据结构与算法、逻辑思维…

opencv——傅里叶变换、低通与高通滤波及直方图等操作

1、傅里叶变换a、傅里叶变换原理时域分析&#xff1a;以时间为参照进行分析。频域分析&#xff1a;相当于上帝视角一样&#xff0c;看事物层次更高&#xff0c;时域的运动在频域来看就是静止的。eg&#xff1a;投球——时域分析&#xff1a;第1分钟投了3分&#xff0c;第2分钟投…

“学好英语网”首页制作

“学好英语网”首页制作一、实验名称&#xff1a;二、实验日期&#xff1a;三、实验目的&#xff1a;四、实验内容&#xff1a;五、实验步骤&#xff1a;六、实验结果&#xff1a;七、源程序&#xff1a;八、心得体会&#xff1a;一、实验名称&#xff1a; “学好英语网”首页…

Linux第三讲

目录 三、 磁盘和文件管理和使用检测和维护 3.1 磁盘目录 3.2 安装软件 3.2.1 rpm命令 3.2.2 克隆虚拟机 3.2.3 yum或压缩包方式安装jdk 3.2.4 使用虚拟机运行SpringBoot项目 3.2.5 安装mysql80&#xff08;57&#xff09; 3.2.6 运行web项目 3.2.7 安装tomcat 三、 …

情人节前夕,竞品在小红书平台如何布局营销策略?

情人节作为全球性消费型节日之一&#xff0c;其营销价值不言而喻。以女性用户群体为主导的小红书平台&#xff0c;更是成为该营销节点众多品牌争夺流量的阵地。 那么&#xff0c;情人节前夕竞品在小红书平台布局什么样的营销策略&#xff1f;创作何种内容&#xff0c;如何推广&…

手把手教你用Python做可视化数据,还能调节动画丝滑度

数据可视化动画还在用Excel做&#xff1f; 现在一个简单的Python包就能分分钟搞定&#xff01; 而且生成的动画也足够丝滑&#xff0c;效果是酱紫的&#xff1a; 这是一位专攻Python语言的程序员开发的安装包&#xff0c;名叫Pynimate。 目前可以直接通过PyPI安装使用。 使用…

线程池小结

什么是线程池 线程池其实就是一种多线程处理形式&#xff0c;处理过程中可以将任务添加到队列中&#xff0c;然后在创建线程后自动启动这些任务。这里的线程就是我们前面学过的线程,这里的任务就是我们前面学过的实现了Runnable或Callable接口的实例对象; 为什么使用线程池 …

1001. x+y 1002. x+y+z etiger.vip 解析与答案

目录 1001题 题目描述 输入输出格式 输入格式 输出格式 输入输出样例 输入样例#1: 输出样例#1: 头文件和数组等初始定义 第一个函数——converts 第二个函数——add 第三个函数——print 主函数部分 完整代码 1002题 题目描述 输入输出格式 输入格式 输出格…

Vue3快速入门【一】

Vue3快速入门一、Vue脚手架1.1、Vite简介1.2、创建项目二、更换Vue模板支持工具三、项目相关命令解析四、生命周期钩子函数五、ref方法的几种使用方式5.1、ref方法(操作基本类型数据)5.2、ref方法(操作复杂类型数据)5.3、ref方法获取标签六、reacttive方法和toRefs方法七、setu…