多线程程序中操作的原子性

0. 背景

原子操作就是不可再分的操作。在多线程程序中原子操作是一个非常重要的概念,它常常用来实现一些同步机制,同时也是一些常见的多线程Bug的源头。

本文主要讨论了三个问题:

1. 多线程程序中对变量的读写操作是否是原子的?

2. 多线程程序中对Bit field(位域)的读写操作是否是线程安全的?

3. 程序员该如何使用原子操作?

我们先从一道很热门的百度笔试题讲起。很多人讲不清楚其背后的原理,下面我们就来对它进行一下剖析(其实这个题目有点歧义,后面我们会讲到):

以下多线程对int型变量x的操作,哪几个需要进行同步:( )
A. x=y; B. x ; C. x; D. x=1;

要彻底理解这个问题,我们首先需要从硬件讲起。以常见的X86 CPU来说,根据Intel的参考手册,它基于以下三种机制保证了多核中加锁的原子操作(8.1节):

(1)Guaranteed atomic operations (注:8.1.1节有详细介绍)
(2)Bus locking, using the LOCK# signal and the LOCK instruction prefix
(3)Cache coherency protocols that ensure that atomic operations can be carried out on cached data structures (cache lock); this mechanism is present in the Pentium 4, Intel Xeon, and P6 family processors

这三个机制相互独立,相辅相承。简单的理解起来就是:

(1)一些基本的内存读写操作是本身已经被硬件提供了原子性保证(例如读写单个字节的操作);
(2)一些需要保证原子性但是没有被第(1)条机制提供支持的操作(例如read-modify-write)可以通过使用”LOCK#”来锁定总线,从而保证操作的原子性
(3)因为很多内存数据是已经存放在L1/L2 cache中了,对这些数据的原子操作只需要与本地的cache打交道,而不需要与总线打交道,所以CPU就提供了cache coherency机制来保证其它的那些也cache了这些数据的processor能读到最新的值。

那么CPU对哪些(1)中的基本的操作提供了原子性支持呢?根据Intel手册8.1.1节的介绍:

从Intel486 processor开始,以下的基本内存操作是原子的:

• Reading or writing a byte(一个字节的读写)
• Reading or writing a word aligned on a 16-bit boundary(对齐到16位边界的字的读写)
• Reading or writing a doubleword aligned on a 32-bit boundary(对齐到32位边界的双字的读写)

从Pentium processor开始,除了之前支持的原子操作外又新增了以下原子操作:

• Reading or writing a quadword aligned on a 64-bit boundary(对齐到64位边界的四字的读写)
• 16-bit accesses to uncached memory locations that fit within a 32-bit data bus(未缓存且在32位数据总线范围之内的内存地址的访问)

从P6 family processors开始,除了之前支持的原子操作又新增了以下原子操作:

• Unaligned 16-, 32-, and 64-bit accesses to cached memory that fit within a cache line(对单个cache line中缓存地址的未对齐的16/32/64位访问)

需要注意的是尽管从P6 family开始对一些非对齐的读写操作已经提供了原子性保障,但是非对齐访问是非常影响性能的,需要尽量避免。当然了,对于一般的程序员来说不需要太担心这个,因为大部分编译器会自动帮你完成内存对齐。

回到最开始那个笔试题。我们先反汇编一下看看它们到底执行了什么操作:

x = y;mov eax,dword ptr [y]mov dword ptr [x],eax
x  ;mov eax,dword ptr [x]add eax,1mov dword ptr [x],eaxx;mov eax,dword ptr [x]add eax,1mov dword ptr [x],eax
x = 1;mov dword ptr [x],1

(1)很显然,x=1是原子操作。


因为x是int类型,32位CPU上int占32位,在X86上由硬件直接提供了原子性支持。实际上不管有多少个线程同时执行类似x=1这样的赋值语句,x的值最终还是被赋的值(而不会出现例如某个线程只更新了x的低16位然后被阻塞,另一个线程紧接着又更新了x的低24位然后又被阻塞,从而出现x的值被损坏了的情况)。

(2)再来看x 和 x。


其实类似x , x =2, x这样的操作在多线程环境下是需要同步的。因为X86会按三条指令的形式来处理这种语句:从内存中读x的值到寄存器中,对寄存器加1,再把新值写回x所处的内存地址(见上面的反汇编代码)。

例如有两个线程,它们按照如下顺序执行(注意读x和写回x是原子操作,两个线程不能同时执行):

time    Thread 1         Thread 20      load eax, x1                            load eax, x2      add eax, 1        add eax, 13      store x, eax4                            store x, eax

我们会发现最终x的值会是1而不是2,因为Thread 1的结果被覆盖掉了。这种情况下我们就需要对x 这样的操作加锁(例如Pthread中的mutex)以保证同步,或者使用一些提供了atomic operations的库(例如Windows API中的atomic库,Linux内核中的atomic.h,Java concurrent库中的Atomic Integer,C 0x中即将支持的atomic_int等等,这些库会利用CPU提供的硬件机制做一层封装,提供一些保证了原子性的API)。

(3)最后来看看x=y。


在X86上它包含两个操作:读取y至寄存器,再把该值写入x。读y的值这个操作本身是原子的,把值写入x也是原子的,但是两者合起来是不是原子操作呢?我个人认为x=y不是原子操作,因为它不是不可再分的操作。但是它需要不需要同步呢?其实问题的关键在于程序的上下文。

例如有两个线程,线程1要执行{y = 1; x = y;},线程2要执行{y = 2; y = 3;},假设它们按如下时间顺序执行:

time    Thread 1        Thread 20        store y, 11                            store y, 22        load eax, y3                            store y, 34        store x, eax

那么最终线程1中x的值为2,而不是它原本想要的1。我们需要加上相应的同步语句确保y = 2不会在线程1的两条语句之间发生。y = 3那条语句尽管在load y和store x之间执行,但是却不影响x=y这条语句本身的语义。所以你可以说x=y需要同步,也可以说x=y不需要同步,看你怎么理解题意了。x=1是否需要同步也是一样的道理,虽然它本身是原子操作,但是如果有另一个线程要读x=1之后的值,那肯定也需要同步,否则另一个线程读到的就是x的旧值而不是1了。

2. 对Bit field(位域)的读写操作是否是线程安全的?

Bit field常用来高效的存储有限位数的变量,多用于内核/底层开发中。一般来说,对同一个结构体内的不同bit成员的多线程访问是无法保证线程安全的。

例如Wikipedia中的如下例子:

struct foo {    int flag : 1;    int counter : 15;};
struct foo my_foo;
/* ... */
/* in thread 1 */
pthread_mutex_lock(&my_mutex_for_flag);my_foo.flag = !my_foo.flag;pthread_mutex_unlock(&my_mutex_for_flag);
/* in thread 2 */
pthread_mutex_lock(&my_mutex_for_counter);  my_foo.counter;pthread_mutex_unlock(&my_mutex_for_counter);

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

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

相关文章

2018秋计算机基础在线作业华师,18秋华师《计算机基础》在线作业3(标准答案).doc...

【奥鹏】[华中师范大学]华师《计算机基础》在线作业试卷总分:100 得分:100第1题,控制面板可实现__________。A、对计算机全面控制操作B、对硬件驱动、软件设置及Windows外观设置C、计算机的关闭操作D、删除计算机中的任意文件正确答案:B第2题,页眉和页脚的建立方法相似&#xf…

.net mvc actionresult 返回字符串_ASP.NET Core中的Action的返回值类型

在Asp.net Core之前所有的Action返回值都是ActionResult,Json(),File()等方法返回的都是ActionResult的子类。并且Core把MVC跟WebApi合并之后Action的返回值体系也有了很大的变化。ActionResult类ActionResult类是最常用的返回值类型。基本沿用了之前Asp.net MVC的那…

.jdeveloper_在JDeveloper 12.1.3中为WebSocket使用Java API

.jdeveloper介绍 最新版本的JDeveloper 12c(12.1.3.0)和WebLogic Server 12.1.3一起提供了一些新的Java EE 7功能。 其中之一是对用于WebSocket的JSR 356 Java API的支持。 实际上,从12.1.2.0版本开始就支持WebSocket协议(RFC 645…

为什么程序员需要关心顺序一致性,而不是 Cache 一致性?

本文所讨论的计算机模型是Shared Memory Multiprocessor,即我们现在常见的共享内存的多核CPU。本文适合的对象是想用C 或者Java进行多线程编程的程序员。本文主要包括对Sequential Consistency和Cache Coherence的概念性介绍并给出了一些相关例子,目的是…

南科大计算机科学与技术专业如何,广州大学、深圳大学、汕头大学、南方科技大学,如何排名?...

广州大学、深圳大学、汕头大学和南方科技大学都是广东省内的一流大学。为了方便各位广东考生在填报志愿的时候有一个更好的了解,顺哥收集整理了这4所学校的一些信息。希望能帮助到大家。深圳大学2021年校友会排名省内第4,全国第57,中国一流大…

canoco5冗余分析步骤_打造高性能的大数据分析平台

大数据时代,大数据的应用与挖掘,大数据的分析和决策,大数据在经济社会的运行轨道上发挥着愈来愈重要的作用。对于大数据分析,现在好多互联网金融公司和传统的商业银行、证券基金公司都非常看重。个个都想在大数据分析中获得重要信…

C 迭代器iterator的实现原理

在经典的设计模式中,有一种迭代器模式,定义为:提供一个对象来顺序访问聚合对象中的一系列数据,而不暴露聚合对象的内部表示。迭代器的主要优点如下:访问一个聚合对象的内容而无须暴露它的内部表示。遍历任务交由迭代器…

html如何在画布上加层,在Canvas中嵌套Html

大概是这样的,现在需要根据一下上传的图片以及一些输入生成图片。本来打算用imagemagick的,但是后来觉得这样前后端要搞两份不同的代码,然后imagemagick使用起来远没有canvas用起来顺手啊。So,最终决定就用Canvas搞定它了&#xf…

如何用illustrator做技术手册_做期货用什么技术指标分析?

来源:期汇股金作者:DC链接:做期货用什么技术指标分析?投资期货市场首先我们要有一套自己的技术分析,那么我们有什么样的技术指标分析最准确呢,没有最准确的技术指标,要看你运用的程度&#xff0…

根据字符串自动构造对应类

问题的起因是,我在做一个demo,有一个对象基类,以及一堆派生出的子对象,比如球体、立方体之类的对象。还有一个对象管理类,用于存储场景中的所有对象。那么在初始化的时候,代码是这么写的:class …

openshift k8s_带有DIY的Openshift上的Spring Boot / Java 8 / Tomcat 8

openshift k8sDIY盒带是一种实验性盒带,提供了一种在OpenShift上测试不受支持的语言的方法。 它提供了最小限度的自由形式的支架,将墨盒的所有细节留给了应用程序开发人员 。 这篇博客文章说明了结合了PostgreSQL服务的Spring Boot / Java 8 / Tomcat 8应…

都兰县第一中学计算机,都兰县第一中学教案.doc

PAGE \* MERGEFORMATPAGE \* MERGEFORMAT 1都兰县第一中学教案班级初一.班周次9时间45分钟课时2授课教师席得勋教学内容篮球:胸前双手传接球器 材篮球25个、栏架4个、垫子4个、长凳4个、标志桶4个教学目标运动参与目标:通过学习激发学生兴趣,使学生积极参…

.sql文件如何执行_mysql:一条SQL查询语句是如何执行的?

本篇文章将通过一条 SQL 的执行过程来介绍 MySQL 的基础架构。首先有一个 user_info 表,表里有一个 id 字段,执行下面这条查询语句:select * from user_info where id 1;返回结果为:-------------------------------------------…

jooq和jdbc_在jOOQ之上构建的RESTful JDBC HTTP服务器

jooq和jdbcjOOQ生态系统和社区正在持续增长。 我们个人总是很高兴看到基于jOOQ构建的其他开源项目。 今天,我们非常高兴为您介绍BjrnHarrtell结合REST和RDBMS的一种非常有趣的方法。 BjrnHarrtell从小就是瑞典的程序员。 他通常在Sweco Position AB上忙于编写GIS系…

C 虚函数表及多态内部原理详解

C 中的虚函数的作用主要是实现了多态的机制。关于多态,简而言之就是用父类型别的指针指向其子类的实例,然后通过父类的指针调用实际子类的成员函数。这种技术可以让父类的指针有“多种形态”,这是一种泛型技术。虚函数表每个含有虚函数的类都…

html代码编辑器sp,在线HTML编译,文本关键字高亮显示,富文本编辑实现大概思路...

????最近被安排做了一个HTML在线编译功能,也利用这个机会对HTML在线编译,关键字高亮,富文本编辑器等的实现做了一些比较表面的研究,做简要记录,以便再次遇到作为参考。????在线HTML编译????首先需要一个能…

numpy 平方_NumPy入门指南

本文介绍了NumPy的基础知识,NumPy是使用Python进行科学计算的软件包。我们将在此处介绍几类基本的数组操作: 创建NumPy数组 重塑数组 NumPy的数学运算 数组的索引和切片 遍历数组首先,让我们将NumPy导入为np。 这使我们可以使用快捷方式np来引…

android 揭示动画_揭示垃圾收集暂停的时间长度

android 揭示动画有几种方法可以改善您的产品。 一种这样的方法是仔细跟踪用户的体验并在此基础上进行改进。 我们确实自己应用了此技术,并再次花了一些时间查看不同的数据 除了我们追求的许多其他方面之外,我们还提出了一个问题“延迟GC触发应用程序的…

10 张程序员喜爱的壁纸,需要自取~

喜欢的话就请点个再看,分享到朋友圈吧~

iptables 开放远程_JavaWeb项目的部署以及远程调试

不点蓝字,我们哪来故事?Linux环境下软件的安装Linux环境下的程序的安装、更新、卸载和查看。rpm 命令:相当于windows程序的添加/卸载程序,进程程序的安装,查看,卸载。本地程序安装:rpm -ivh 程序…