• Interlocked原子函数陷阱


    一、问题

    windows api函数中提供了InterlockedExchange、InterlockedDecrement, InterlockedIncrement, ExInterlockedAddLargeInteger, ExInterlockedAddUlong等原子访问函数,在众多线程同步方法中效率最高。

     最近在工作中,在类A中添加了两个变量作为标志位,用于多线程间的标志同步用,所以用到了InterlockedExchange函数,类似如下代码:

    //.h文件
    class A{
    public:
    void SetFirstFlag(const bool &flag);//设置标志位a
    bool GetFirstFlag();//获取标志a的值
    
    void SetSecondFlag(const bool& flag);//设置标志位b
    bool GetSecondFlag();//获取标志b的值
    private:
    bool a;
    bool b;
    };
    
    //.cpp文件
    A::A(void){
        a = true;
        b = true;
    }
    A::~A(void){
      
    }
    
    void A::SetFirstFlag(const bool& flag){//设置标志位a
        InterlockedExchange((unsigned long*)&a, flag);
    }
    
    bool A::GetFirstFlag(){//获取标志a的值
        if (InterlockedExchange((unsigned long*)&a, false) == true)
        {
            SetFirstFlag(true);//恢复为原来的值
            return true;
        }
    
        return false;
    }
    void A::SetSecondFlag(const bool& flag){//设置标志位b
        InterlockedExchange((unsigned long*)&b, flag);
    }
    
    bool A::GetSecondFlag(){//获取标志b的值
        if (InterlockedExchange((unsigned long*)&b, false) == true)
        {
            SetSecondFlag(true);//恢复为原来的值
            return true;
        }
    
        return false;
    } 

    这个类实现的功能很简单,调用SetFirstdFlag或者SetSecondFlag用于设置a或者b的值,调用GetFirstFlag或者GetSecondFlag获取a或者b的值。

    但是在实际调试过程中发现,明明在构造函数函数中设置了a为true,但是在第一次进入到相应的SetSecondFlag或者GetSecondFlag函数中时,却发现b的值为false,而且完全不受控制,一会儿true一会儿为false,搞的我都怀疑人生了。

    在耽误了一下午后,发现问题出在了指针操作上。在win32下,bool型占用1字节数据,a和b在内存上相邻,各占用1字节,在类A调用构造函数初始化后,内存内容是:

    a和b的值都是1,但是这里函数InterlockedExchange的实际定义是:

    FORCEINLINE
    unsigned long
    InterlockedExchange(
        __inout __drv_interlocked unsigned long volatile *Target,
        __in unsigned long Value
        )
    {
        return (unsigned long) InterlockedExchange((volatile long*) Target, (long) Value);
    }

    很明显,被改变的变量是以long指针操作修改值的,所以每次调用InterlockedExchange((unsigned long*)&a, false)时,是将a的值赋值给了a的地址指向的4字节内存,所以b的值也被篡改了,但是修改b的值不会影响a的值。

    到这里,很明显,是调用api函数的时候没注意传入参数的含义,也没注意数据类型占用的字节数导致该问题。将类A中将所有bool修改为BOOL后,因为BOOL和long都是占用4字节内存,问题就得以解决了。

    二、总结

    在调用api函数的时候一定要注意参数含义,尤其是指针操作的时候,要注意参数占用字节数,调用方式错误,轻则导致数据逻辑错误,重则软件崩溃。

  • 相关阅读:
    安装系统时碰上hal.dll文件丢失或损坏问题
    系统无法停止USB接口的“通用卷”的解决方法
    提取嵌入excel或word中flash的VBA代码
    打印机的PCL驱程和PS驱程的区别!
    spoolsv.exe占用资源的解决方法
    检测硬盘与内存中的隐藏病毒
    关于Windows默认共享的一些认识
    SmartClient + WebServices 开发 1
    常用 ajax 框架比较摘自网络
    AJAX 框架 Prototype
  • 原文地址:https://www.cnblogs.com/chenyangchun/p/6869320.html
Copyright © 2020-2023  润新知