定义1:
原子操作(atomic operation)是不需要synchronized。
原子操作是不可分割的,在执行完毕不会被任何其它任务或事件中断。在单处理器系统(UniProcessor)中,能够在单条指令中完成的操作都可以认为是" 原子操作",因为中断只能发生于指令之间。在对称多处理器(Symmetric Multi-Processor)结构中就不同了,由于系统中有多个处理器在独立地运行,即使能在单条指令中完成的操作也有可能受到干扰。
Linux下面,整数的自增和自减操作的汇编程序为:
自增:
movl X(%rip),%eax
Addl $1,%eax
Movl %eax,X(%rip)
自减:
movl X(%rip),%eax
subl $1,%eax
Movl %eax,X(%rip)
从汇编程序来看自增与自减操作不是原子操作。如果两个线程,在自增第一步被中断,那么现场文件中的X是相同的,但是当其恢复时,每个线程都会从自己的现场X开始增加,这样导致X的改变并非同步。
对于在一个生产者一个消费者的情况下,使用循环数组可以不用锁。这主要是因为写操作必须完成第三步才会将自增值写回内存。所以读操作永远读取的都是真实的内存中变量的值,不需要加锁操作。但是当一个写,多个读时,那么读者读取的值可能就不会完全一样了。这个很容易看出来。
实验:
- 单核线程操作:
整数变量gi,每个线程自加100w次,理论结果应该是200W。实验结果值并不为200W。
实验结果如下:
View Code
[root@cs lib]# ./atomic this process 5181 is running processor:0 this process 5180 is running processor:0 2000000 [root@cs lib]# ./atomic this process 5184 is running processor:0 this process 5183 is running processor:0 2000000 [root@cs lib]# ./atomic this process 5187 is running processor:0 this process 5186 is running processor:0 1988073 [root@cs lib]# ./atomic this process 5189 is running processor:0 this process 5190 is running processor:0 1984160 [root@cs lib]# ./atomic this process 5193 is running processor:0 this process 5192 is running processor:0 2000000
图1 单核多线程自增操作
- 多核线程操作:
整数变量gi,每个线程自加100w次,理论结果应该是200W。
实验结果如下:
View Code
[root@cs lib]# ./atomic this process 5223 is running processor:0 this process 5224 is running processor:1 1582784 [root@cs lib]# ./atomic this process 5226 is running processor:0 this process 5227 is running processor:1 1583863 [root@cs lib]# ./atomic this process 5230 is running processor:1 this process 5229 is running processor:0 1615426 [root@cs lib]# ./atomic this process 5232 is running processor:0 this process 5233 is running processor:1 1585477 [root@cs lib]# ./atomic this process 5236 is running processor:1 this process 5235 is running processor:0 1584724 [root@cs lib]# ./atomic this process 5239 is running processor:0 this process 5240 is running processor:1 1764117
图2 多核多线程自增结果
通过上面两个实验可以看出,自增和自减操作并非原子操作。
附:实验代码
View Code
#include<stdio.h> #define __USE_GNU #include<sched.h> #include<ctype.h> #include<unistd.h> #include<sys/sysinfo.h> #include<sys/types.h> #include<stdlib.h> #include<string.h> #include<pthread.h> #include <sys/syscall.h> #define gettid() syscall(__NR_gettid) static unsigned long gi; void sslep() { unsigned long i=0; while(i<100) i++; } void write_pthread() { cpu_set_t mask; cpu_set_t get; unsigned long i; CPU_ZERO(&mask); CPU_SET(0,&mask); if(sched_setaffinity(0,sizeof(mask),&mask) == -1){ printf("warning:could not set CPU affinity,continue...\n"); } { CPU_ZERO(&get); if(sched_getaffinity(0,sizeof(get),&get) == -1){ printf("warning:could not get CPU affinity,continue...\n"); } for(i=0;i<1000000;i++){ if(CPU_ISSET(i,&get)){ printf("this process %d is running processor:%d\n",gettid(),i); } sslep(); gi++; } } } void read_pthread() { cpu_set_t mask; cpu_set_t get; unsigned long i; CPU_ZERO(&mask); CPU_SET(1,&mask); if(sched_setaffinity(0,sizeof(mask),&mask) == -1){ printf("warning:could not set CPU affinity,continue...\n"); } { CPU_ZERO(&get); if(sched_getaffinity(0,sizeof(get),&get) == -1){ printf("warning:could not get CPU affinity,continue...\n"); } for(i=0;i<1000000;i++){ if(CPU_ISSET(i,&get)){ printf("this process %d is running processor:%d\n",gettid(),i); } sslep(); gi++; } } } int main() { pthread_t pt[2]; int i,ret; ret = pthread_create(&pt[0],NULL,(void*)write_pthread,NULL); if(ret != 0){ printf("create write thread error\n"); exit(1); } ret = pthread_create(&pt[1],NULL,(void*)read_pthread,NULL); if(ret != 0){ printf("create write thread error\n"); exit(1); } for(i=0;i<2;i++){ pthread_join(pt[i],NULL); } printf("%ld\n",gi); return 0; }