自增与自减的原子性

定义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的改变并非同步。

对于在一个生产者一个消费者的情况下,使用循环数组可以不用锁。这主要是因为写操作必须完成第三步才会将自增值写回内存。所以读操作永远读取的都是真实的内存中变量的值,不需要加锁操作。但是当一个写,多个读时,那么读者读取的值可能就不会完全一样了。这个很容易看出来。

实验:

  1. 单核线程操作

整数变量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 单核多线程自增操作

  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;
}
原文地址:https://www.cnblogs.com/gogly/p/2749596.html