zoukankan      html  css  js  c++  java
  • 多线程--原子操作 Interlocked系列函数

    [转]原文地址:http://blog.csdn.net/morewindows/article/details/7429155

    线程同步与互斥: 互斥主要指多个线程不能同时访问一个资源,如打印机就是互斥资源。同步是指多个线程要按一定的次序访问,如上餐馆吃饭,只有菜上桌后你才能吃。互斥其实是一种特殊的同步。

    先看一个程序:

    #include <stdio.h>  
    #include <process.h>  
    #include <windows.h>  
    volatile long g_nLoginCount; //登录次数  
    const int THREAD_NUM = 10; //启动线程数  
    unsigned int __stdcall ThreadFun(void *pPM)  
    {   //使用了sleep是为了使得每个线程的执行之间有延迟,从而产生线程之间的干扰
        Sleep(100); //some work should to do  
        g_nLoginCount++;  
        Sleep(50);   
        return 0;  
    }  
    int main()  
    {  
        g_nLoginCount = 0;  
      
        HANDLE  handle[THREAD_NUM];  
        for (int i = 0; i < THREAD_NUM; i++)  
            handle[i] = (HANDLE)_beginthreadex(NULL, 0, ThreadFun, NULL, 0, NULL);  
          
        WaitForMultipleObjects(THREAD_NUM, handle, TRUE, INFINITE);   
        printf("有%d个用户登录后记录结果是%d
    ", THREAD_NUM, g_nLoginCount);  
        return 0;  
    }  
    

    程序中模拟的是10个用户登录,程序将输出结果:

    程序输出的结果好象并没什么问题。下面我们增加点用户来试试,现在模拟50个用户登录,为了便于观察结果,在程序中将50个用户登录过程重复20次,代码如下:

    #include <stdio.h>  
    #include <windows.h>  
    volatile long g_nLoginCount; //登录次数  
    const DWORD THREAD_NUM = 50;//启动线程数  
    DWORD WINAPI ThreadFun(void *pPM)  
    {  
        Sleep(100); //some work should to do  
        g_nLoginCount++;  
        Sleep(50);  
        return 0;  
    }  
    int main()  
    {  
        printf("     原子操作 Interlocked系列函数的使用
    ");  
        printf(" -- by MoreWindows( http://blog.csdn.net/MoreWindows ) --
    
    ");  
          
        //重复20次以便观察多线程访问同一资源时导致的冲突  
        int num= 20;  
        while (num--)  
        {     
            g_nLoginCount = 0;  
            int i;  
            HANDLE  handle[THREAD_NUM];  
            for (i = 0; i < THREAD_NUM; i++)  
                handle[i] = CreateThread(NULL, 0, ThreadFun, NULL, 0, NULL);  
            WaitForMultipleObjects(THREAD_NUM, handle, TRUE, INFINITE);  //最大等待数为64
            printf("有%d个用户登录后记录结果是%d
    ", THREAD_NUM, g_nLoginCount);  
        }  
        return 0;  
    }  
    

    运行结果如下图:

    现在结果水落石出,明明有50个线程执行了g_nLoginCount++;操作,但结果输出是不确定的,有可能为50,但也有可能小于50。

    要解决这个问题,我们就分析下g_nLoginCount++;操作。在VC6.0编译器对g_nLoginCount++;这一语句打个断点,再按F5进入调试状态,然后按下Debug工具栏的Disassembly按钮,这样就出现了汇编代码窗口。可以发现在C/C++语言中一条简单的自增语句其实是由三条汇编代码组成的,如下图所示。

    讲解下这三条汇编意思:

    第一条汇编将g_nLoginCount的值从内存中读取到寄存器eax中。

    第二条汇编将寄存器eax中的值与1相加,计算结果仍存入寄存器eax中。

    第三条汇编将寄存器eax中的值写回内存中。

           这样由于线程执行的并发性,很可能线程A执行到第二句时,执行B,假设B执行结束后,继续执行A,其实寄存器eax是会恢复到A最后的值,这样导致的结果是线程B的执行结果被A覆盖,相当于B没有执行。这样执行下来,结果是不可预知的——可能会出现50,可能小于50。(每个线程的寄存器是私有的,切换线程时会保存各寄存)

           因此在多线程环境中对一个变量进行读写时,我们需要有一种方法能够保证对一个值的递增操作是原子操作——即不可打断性,一个线程在执行原子操作时,其它线程必须等待它完成之后才能开始执行该原子操作。这种涉及到硬件的操作会不会很复杂了,幸运的是,Windows系统为我们提供了一些以Interlocked开头的函数来完成这一任务(下文将这些函数称为Interlocked系列函数)。

    下面列出一些常用的Interlocked系列函数:

    1.增减操作

    LONG__cdecl InterlockedIncrement(LONG volatile* Addend);

    LONG__cdecl InterlockedDecrement(LONG volatile* Addend);

    返回变量执行增减操作之后的值。

    LONG__cdec InterlockedExchangeAdd(LONG volatile* Addend, LONGValue);

    返回运算后的值,注意!加个负数就是减。

    2.赋值操作

    LONG__cdecl InterlockedExchange(LONG volatile* Target, LONGValue);

    Value就是新值,函数会返回原先的值。

     

    在本例中只要使用InterlockedIncrement()函数就可以了。将线程函数代码改成:

    DWORD WINAPI ThreadFun(void *pPM)  
    {  
        Sleep(100);//some work should to do  
        //g_nLoginCount++;  
        InterlockedIncrement((LPLONG)&g_nLoginCount);  
        Sleep(50);  
        return 0;  
    }  

    再次运行,可以发现结果会是唯一的。

     因此,在多线程环境下,我们对变量的自增自减这些简单的语句也要慎重思考,防止多个线程导致的数据访问出错。

  • 相关阅读:
    atitit.nfc 身份证 银行卡 芯片卡 解决方案 attilax总结
    atitit.php 流行框架 前三甲为:Laravel、Phalcon、Symfony2 attilax 总结
    Atitit.执行cmd 命令行 php
    Atitit. 图像处理jpg图片的压缩 清理垃圾图片 java版本
    atitit。企业组织与软件工程的策略 战略 趋势 原则 attilax 大总结
    atitit. 管理哲学 大毁灭 如何防止企业的自我毁灭
    Atitit.java的浏览器插件技术 Applet japplet attilax总结
    Atitit.jquery 版本新特性attilax总结
    Atitit. 软件开发中的管理哲学一个伟大的事业必然是过程导向为主 过程导向 vs 结果导向
    (转)获取手机的IMEI号
  • 原文地址:https://www.cnblogs.com/P3nguin/p/7885411.html
Copyright © 2011-2022 走看看