zoukankan      html  css  js  c++  java
  • windows多线程--原子操作

    推荐参考博客:秒杀多线程第三篇 原子操作 Interlocked系列函数

                       原子操作 VS 非原子操作                   

    原子操作就是不会被线程调度机制打断的操作,这种操作一旦开始,就一直运行到结束,中间不会有任何线程切换。                                          本文地址

    首先从一个简单的例子来看,1000个线程同时对一个全局变量(初始化为0)做++操作,最后我们期望的这个变量的值是1000,但是有时候结果却事与愿违:

     #include<string>
     #include<iostream>
     #include<process.h>
     #include<windows.h>
     using namespace std;
    
    volatile int g_cnt;
    
    unsigned __stdcall threadFun(void *param)
    {
        g_cnt++;
        return 0;
    }
    
    int main()
    {
        for(int j = 0; j < 100; j++)
        {
            g_cnt = 0;
            const int threadNum = 1000;
            HANDLE hth[threadNum];
    
            for(int i = 0; i < threadNum; i++)
                hth[i] = (HANDLE)_beginthreadex(NULL, 0, threadFun, NULL, 0, NULL);
    
            //注意WaitForMultipleObjects每次最多等待MAXIMUM_WAIT_OBJECTS个object;
            //也可以调用1000次WaitForSingleObject
            int k = threadNum / MAXIMUM_WAIT_OBJECTS;
            for(int i = 0; i < k; i++)
                WaitForMultipleObjects(MAXIMUM_WAIT_OBJECTS,
                                       &hth[i*MAXIMUM_WAIT_OBJECTS],
                                       TRUE, INFINITE);
            if(threadNum % MAXIMUM_WAIT_OBJECTS != 0)
                WaitForMultipleObjects(threadNum % MAXIMUM_WAIT_OBJECTS,
                                       &hth[k*MAXIMUM_WAIT_OBJECTS],
                                       TRUE, INFINITE);
    
            for(int i = 0; i < threadNum; i++)
                CloseHandle(hth[i]);
    
            if(g_cnt != 1000)
                cout<<"the value of g_cnt: "<<g_cnt<<endl;
        }
    }

    image

    为什么会这样呢,因为g_cnt++不是一个原子操作,在vs2010中查看反汇编代码为:

    image

    对于++操作,编译器把它分为三步:1、从内存中吧g_cnt的值读到寄存器eax,2、eax中的值+1, 3、把eax中的值写会内存

    如果有两个线程,线程1执行到第二条语句时,线程2开始执行,那么线程2获取的g_cnt的值还是原来的0(因为线程1还没有执行低三条语句来写回内存),最后g_cnt的值就是1,而不是期望的2;

    windows系统提供给了一些函数来保证某些操作的原子性:

    LONG __cdecl InterlockedIncrement(LONG volatile* Addend); //变量加1

    LONG __cdecl InterlockedDecrement(LONG volatile* Addend);//变量减1

    LONG __cdec InterlockedExchangeAdd(LONG volatile* Addend, LONG Value);//变量加上value

    LONG __cdecl InterlockedExchange(LONG volatile* Target, LONG Value);//将value的值 赋值 给target指向的变量

    以上列出的函数是针对32位的LONG数据的,如果是64位的数据,有其对应的函数,具体可以参考 msdn 列出的所有原子操作函数

    针对上面的问题,我们可以把g_cnt++; 改为 InterlockedIncrement((LONG volatile *)&g_cnt);

    【版权声明】转载请注明出处:http://www.cnblogs.com/TenosDoIt/p/3602426.html

  • 相关阅读:
    uni-app中使用Echarts绘画图表
    css设置Overflow实现隐藏滚动条的同时又可以滚动
    JS判断在哪一端浏览器打开
    ElementUI分页Pagination自动到第一页
    第203场周赛
    TreeSet使用
    155 最小栈
    234 回文链表
    141 环形链表
    2 两数相加
  • 原文地址:https://www.cnblogs.com/TenosDoIt/p/3602426.html
Copyright © 2011-2022 走看看