免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
楼主: mingjwan
打印 上一主题 下一主题

对int变量的赋值是原子操作吗? [复制链接]

论坛徽章:
0
1 [报告]
发表于 2006-08-09 19:25 |显示全部楼层
原帖由 mingjwan 于 2006-8-4 09:32 发表
请问对int变量的赋值是原子操作吗?
会不会出现一个线程赋了两个字节,另一个线程来读取数据的情况呢?


对 int 变量的赋值是否是原子操作与使用的计算机的结构体系有关,同时也与此变量的内存对齐情况有关。

一般的体系结构中都能把一个 int 大小的数据作为一个整体进行处理,也就是说用一条指令完成。对于这样的系统,对 int 变量的赋值是否是原子操作还取决于此数据在内存中是否已经对齐:如果变量是内存对齐的话,那么就有可能是原子操作;如果非内存对齐,那么一个 int 数据要分至少两次才能完成,就不是原子操作了。

但是也有的结构体系不是这样的,如 zu_xf 在上面提到的情况,一个 int 数据要拆分为几部分分别处理,对于这样的体系结构,对 int 赋值就不是原子操作了。

如果不是原子操作,就有可能“出现一个线程赋了两个字节,另一个线程来读取数据的情况”。

论坛徽章:
0
2 [报告]
发表于 2006-08-10 18:01 |显示全部楼层
原帖由 isjfk 于 2006-8-10 08:39 发表

没有对齐的数据虽然要分两个时钟周期才能获取,但是并不影响这条机器指令的原子性。即使这个时候有中断发生,也是在赋值语句完成之后才会进入中断处理程序。


明白了,谢谢指正。看来我一直在这里存在着错误的认识。自己也写了个小程序,发现即使对没有对齐的 int 访问,在写数据的时候是用一条指令来完成的,而不是我以前误认为的对应着两条写指令。

论坛徽章:
0
3 [报告]
发表于 2006-08-11 15:04 |显示全部楼层
原帖由 mq110 于 2006-8-11 13:11 发表
在操作系统的基本课程中早就提到过了 ,

int变量的赋值操作如果翻译成汇编语句不是单条指令的话,那么肯定不是原子的.


一个 C 语句通常对应着一条或者多条指令。如果对应着多条指令,那么这个赋值操作就有可能从中间被中断,这时可以说赋值操作不是原子的。这是一种从指令集角度上的理解。

还可以有另外一种理解,即从赋值操作的实际效果上来考虑问题:只要不会出现楼主所说的部分赋值现象,我们就可以说赋值是原子操作。为此,可以把赋值操作的指令集分为两部分:赋值准备指令和写指令(如果是立即数的赋值可能不需要赋值准备指令)。显然,如果写指令只有一条的话,赋值操作是原子的;否则,写指令多于一条的话就不能保证赋值操作的原子性了。

楼主所提问的应该是后一种理解,也应该是一般意义上的理解。

>> 寄存器的赋值 和 对内存的修改不能一条指令完成的时候, 就要加锁. 因为对int型的变量操作不是原子的.

如果是这样的话,就无法解释在一些平台上出现的 思一克 所说的下面的情况:
  1. 看atomic_t的定义,就定义为一个整数。
  2. atomic_set()和atomic_read()就是一个赋值汇编语句。没有用lock.
复制代码

论坛徽章:
0
4 [报告]
发表于 2006-08-11 22:03 |显示全部楼层
gvim 引用的一段话很好地说明了原子操作的特征:

the set of operations either succeeds or fails all at once. No in-between state is accessible.

然而这两句话可以对赋值操作的原子性产生两种不同的理解(前面已经说过了):一种是赋值操作如果是由多条指令组成的话,由于没有同步保证,可以被中断,所以赋值过程不是原子的。另一种是从赋值的结果(是否存在中间状态)上来考虑。一些争论就是因为持有不同的理解造成的。我们更加关心的是结果,而不是过程,所以赋值的原子性一般指的是后一种情况。

考察一个操作的原子性就是考察此操作结果是否会由于中断而出现一个中间状态(in-between state)。

对于赋值来说,如果它是一个原子操作,则说明要么赋值已经成功,要么尚未进行,不存在只进行了一半的所谓中间状态的情况;如果出现了中间状态,则说明赋值不是一个原子操作。

这是从结果(状态)上来说的。至于由一条或者多条指令组成赋值过程是否是原子过程并不重要,关键是看其中的向赋值操作的左操作数中的写操作是由一条还是几条指令组成的:如果这样的写指令只有一条,则赋值是原子性的;如果多于一条写指令,则在第一次写操作后如果被中断的话其赋值结果就出现了一个中间状态,因此就不是原子操作。

修改:加上颜色强调一下。

[ 本帖最后由 whyglinux 于 2006-8-12 01:14 编辑 ]

论坛徽章:
0
5 [报告]
发表于 2006-08-11 22:59 |显示全部楼层
原帖由 cellar 于 2006-8-11 22:20 发表
各位可以随便翻一下经典的多线程编程的书籍,几乎每本都在访问共享变量的时候先加锁,再读写,如果赋值操作在操作层面上来说是原子的(先不管这个定义是不是准确,为了说明问题),那这些书都是多此一举的吗?


注意在这里讨论的是对 int 变量的赋值是否是原子的,而且即使是对 int 变量的赋值,并不是在所有的平台上都是原子操作,这时就要进行同步保证(比如加锁)。你说的共享变量的访问情况可能是:1. 对 int 的赋值不是原子的。2. 访问的是长度大于 int 的数据。3. 是对一组变量的访问。上述无论哪种情况都需要同步保证。

论坛徽章:
0
6 [报告]
发表于 2006-08-12 01:00 |显示全部楼层
>> 我到第一次听说原子性还可以从过程和结果两种方式去理解。

这是因为对于语言层面上的一个语句来说,如果没有同步保证的话来讨论它的原子性不是很明智。大部分赋值语句可以对应着一系列的指令,这时显然这个赋值操作过程不是原子的,但是就赋值的结果来说(左操作数有没有出现中间值),可能是原子的,也可能不是。

>> 如你说所,过程不是原子的已经达成共识。

严格来说不是这样的,因为如 x = 123; 这样进行的立即数赋值可能只需要诸如 movl    $123, -4(%ebp) 一条指令,所以无论是赋值过程还是结果都是原子性的。

>> 那么我第二次问你“赋值结果”是什么意思?
>> 你的意思是否是 重点在最后的往左操作数b写入的 movl %eax, b 一句是原子的,那么整个 b=i 的赋值操作就是原子的?

单条指令,如 movl %eax, b 的执行不可被中断,所以其操作,无论结果还是过程,都是原子的。

问题在于(前面也已经提到过),一个赋值语句的向左操作数写入的过程可能不是由一条传送指令、而是由两条或者以上的传送指令组成(比如对于 long long int 变量一般需要要两条传送指令完成赋值。虽然不常见,但是有的系统即使对于 int 变量也是这样)。如果是这种情况,显然赋值不是原子操作。

论坛徽章:
0
7 [报告]
发表于 2006-08-20 22:59 |显示全部楼层
原帖由 oyscal 于 2006-8-20 22:13 发表
如果int a=0;两个线程同时执行一次a++,结果不一定是2,很有可能是1.情况可能会是这样的:
1)线程1将a的值从内存读到寄存器
2)线程1在寄存器里将a++(a=1)
3)线程2此时被调度,将a的值从内存(注意此时内 ...


赋值和自加还是有区别的:赋值是一个读-写的过程,而自加是一个读-改-写的过程,比起赋值来多了一个“改”的环节。

而且你上面说的 a++ 出现的问题只会出现在多CPU结构上,对于单CPU来说应该不存在这个问题。
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP