zoukankan      html  css  js  c++  java
  • 自增与自减的原子性

    定义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;
    }
  • 相关阅读:
    Linkedin工程师是如何优化他们的Java代码的
    如何调试 Android 上 HTTP(S) 流量
    Facebook工程师是如何改进他们Android客户端的
    modified: xxx(modified content, untracked content)
    在Android工程中加入AIDL文件时,gen目录生成的文件报错-问题解决
    Ubuntu14.04LTS下使用eclipse搭建Cocos2d-x的Android环境
    Ubuntu Linux下安装Oracle JDK
    查看Android源码版本
    Ubuntu Linux下安装Oracle JDK
    Android Initializing a Build Environment
  • 原文地址:https://www.cnblogs.com/gogly/p/2749596.html
Copyright © 2011-2022 走看看