zoukankan      html  css  js  c++  java
  • [转]用汇编实现原子操作

    原子操作(1) - 用汇编实现原子操作

    “最轻量级的锁”,通常也叫”原子操作”,之所以加引号是因为他们在汇编级别并不是原子操作,是用多条指令完成的,这些操作大多都是利用CPU支持的汇编指令。

    在某些构架过时的CPU体系结构上,它们应该是用比较重量级的线程内锁实现的吧(我的猜测)。

    最常见的原子操作有Compare and Exchange,Self Increase/Decrease等等。

     

    1、80486 CPU相关指令:

    LOCK:这是一个指令前缀,在所对应的指令操作期间使此指令的目标操作数指定的存储区域锁定,以得到保护。

    XADD:先交换两个操作数的值,再进行算术加法操作。多处理器安全,在80486及以上CPU中支持。

    CMPXCHG:比较交换指令,第一操作数先和AL/AX/EAX比较,如果相等ZF置1,第二操作数赋给第一操作数,否则ZF清0,第一操作数赋给AL/AX/EAX。多处理器安全,在80486及以上CPU中支持。

    XCHG:交换两个操作数,其中至少有一个是寄存器寻址.其他寄存器和标志位不受影响.

    80486以上都支持这四个操作,因此当今几乎100%CPU都支持这两个指令,也能由此用标准C和C++写出一系列几乎可以跨平台的”原子操作”函数和Lock-Free数据结构和算法.

    64位平台也有一系列的相关指令,当然他们也有以上提到的指令,其下的64位原子操作函数应该和32位的分开(要问为什么?我现在告诉你恐怕你印象不深,接着看这一系列吧),而道理完全一样.因此,不存在跨CPU体系结构的问题)

     

    2、原子操作函数:

    由以上提供的几个汇编指令,我们可以做出以下实现,这些都是最常用的原语。

    比较后交换

    long __stdcall CompareExchange(long volatile*Destination,long Exchange,long Comperand)
    {
       __asm
       {
          mov     ecx, Destination;
          mov     edx, Exchange;
          mov     eax, Comperand;
          lock cmpxchg [ecx], edx;
       }
    }

    交换

    long __stdcall Exchange(long volatile* Target,long Value)
    {
       __asm
       {
          mov      ecx, Target;
          mov      edx, Value;
    label:
          lock cmpxchg [ecx], edx;//
          jnz      short label;
       }
    }

    自减

    long __stdcall Decrement(long volatile* Addend)
    {
       __asm
       {
          mov     ecx, Addend;
          mov     eax, 0FFFFFFFFh;//-1
          lock xadd [ecx], eax; //加-1
          dec     eax;
       }
    }

    自增

    long __stdcall Increment(long volatile* Addend)
    {
       __asm
       {
          mov      ecx, Addend;
          mov      eax, 1;
          lock xadd [ecx], eax; //
          inc      eax;
       }
    }

    相加后交换

    long __stdcall ExchangeAdd(long volatile* Addend,long Value)
    {
       __asm
       {
          mov      ecx, Addend;
          mov      eax, Value;
          lock xadd [ecx], eax;
       }
    }

    原子操作(2) - 泛型后的原子操作

    32位的数据类型有4种,但是上面的只支持long,怎么办?手工hack?太土了,当然是C++的大规模杀伤性武器template了。

    同时把几个不跨平台的地方抽出来用macro表示。

    目前模板还没有得到concept的支持,所以只能用boost.type_traits低级的手动判断,要求只有32位的整数类型才能实例化这些函数。

    #include
    #include
    #define CALL_METHOD __stdcall
    #define VOLATILE volatile
    
    template<typename T>
    T CALL_METHOD compare_exchange32(T VOLATILE*Destination,T exchange32,T Comperand)
    {
       BOOST_STATIC_ASSERT(sizeof(T) == 4 && boost::is_integral<T>::value);
       __asm
       {
          mov     ecx, Destination;
          mov     edx, exchange32;
          mov     eax, Comperand;
          lock cmpxchg [ecx], edx;
       }
    }
    
    template<typename T>
    T CALL_METHOD exchange32(T VOLATILE* Target,T Value)
    {
       BOOST_STATIC_ASSERT(sizeof(T) == 4 && boost::is_integral<T>::value);
       __asm
       {
          //     mov    ecx, Target;
          //     mov    edx, Value;      
          //label:
          //     lock   cmpxchg [ecx], edx;////     jnz    short label;
          mov      ecx, Target;
          mov      eax, Value;
          xchg [ecx],eax;
       }
    }
    
    template<typename T>
    T CALL_METHOD decrement32(T VOLATILE* Addend)
    {
       BOOST_STATIC_ASSERT(sizeof(T) == 4 && boost::is_integral<T>::value);
       __asm
       {
          mov     ecx, Addend;
          mov    eax, 0FFFFFFFFh;//-1
          lock xadd [ecx], eax; //加-1
          dec     eax;
       }
    }
    
    template<typename T>
    T CALL_METHOD increment32(T VOLATILE* Addend)
    {
       BOOST_STATIC_ASSERT(sizeof(T) == 4 && boost::is_integral<T>::value);
       __asm
       {
          mov      ecx, Addend;
          mov      eax, 1;
          lock xadd [ecx], eax; //
          inc      eax;
       }
    }
    
    template<typename T>
    T CALL_METHOD exchange_add32(T VOLATILE* Addend,T Value)
    {
       BOOST_STATIC_ASSERT(sizeof(T) == 4 && boost::is_integral<T>::value);
       __asm
       {
          mov      ecx, Addend;
          mov      eax, Value;
          lock xadd [ecx], eax;
       }
    }

    原子操作(3) - 原子数类

    根据上面的5个函数就能做出一个原子操作的整数数字类,这将是下一节中,我的最轻量级锁的基础和原型,他不依赖于操作系统,当然,所以你也可以不叫他是锁,只是一种类似锁的机制.

    一切细节看源码中穿插的注释.

    #ifndef __ATOM_VALUE_H__
    #define __ATOM_VALUE_H__
    
    #include "atom.hpp"
    #include <boost/static_assert.hpp>
    #include <boost/type_traits.hpp>
    
    template<typename T>
    class atomic_value32
    {
       //恩,用boost.type_traits来保证是位的整数类型
       BOOST_STATIC_ASSERT(sizeof(T) == 4 && boost::is_integral<T>::value);
    
    private:
       volatile T value_;
    
    public:
       atomic_value32(T v = 0)
          :value_(v){}
    
       atomic_value32(atomic_value32& v){//??? 这里留给大家,我不给出源码了
       }
    
       //需要这么一个转型符号,因为大部分时候我们将atomic_value32<T>当作一个T使用
       operator T(){return exchange_add32(&value_,0);}
    
       //赋值
       atomic_value32& operator=(T v){exchange32(&value_, v);return *this;}
       atomic_value32& operator=(atomic_value32& v){exchange32(&value_, v);return *this;}
    
       //比较并交换,好像没有什么operator与之对应,就直接拿出来了
       T compare_exchange(T to_exchange, T to_compare)
       {return compare_exchange32<T>(&value_, to_exchange, to_compare);}
    
       //只提供前置,后置似乎没什么必要,我也懒得去实现了:)
       T operator++(){return increment32(&value_);}
       T operator--(){return decrement32(&value_);}
    
       //千万不能返回引用,因为线程安全考虑,
       T operator+=(T add){return exchange_add32(&value_,add);}
       T operator+=(atomic_value32& add){return exchange_add32(&value_,add);}
       T operator-=(T add){return exchange_add32(&value_,-add);}
       T operator-=(atomic_value32& add){return exchange_add32(&value_,-add);}
    
       //6个比较符号
       bool operator==(T rhs){return operator T()==rhs;}
       bool operator==(atomic_value32& rhs){return operator T()==rhs.operator T();}
       bool operator<(T rhs){return operator T()<rhs;}
       bool operator<(atomic_value32& rhs){return operator T()<rhs.operator T();}
       bool operator!=(T rhs){return !this->operator ==(rhs);}
       bool operator!=(atomic_value32& rhs){return !this->operator ==(rhs);}
       bool operator>=(T rhs){return !this->operator <(rhs);}
       bool operator>=(atomic_value32& rhs){return !this->operator <(rhs);}
       bool operator>(T rhs){return ((*this)!=(rhs)) && !((*this)<(rhs));}
       bool operator>(atomic_value32& rhs){return ((*this)!=(rhs)) && !((*this)<(rhs));}
       bool operator<=(T rhs){return !((*this)>(rhs));}
       bool operator<=(atomic_value32& rhs){return !((*this)>(rhs));}
    };
    
    #endif//__ATOM_VALUE_H__

    参考引用:

    http://www.cppblog.com/woaidongmao/archive/2009/10/19/98965.html

  • 相关阅读:
    我的第一个可用的Windows驱动完成了
    据说是一种很古老的方法
    起一卦,测今天工作,问题不少
    起一卦,找房子,马上没房子住了
    哈哈哈哈,我竟然发现了个MSDN里面的笔误
    起一卦,看现在我的工程进度怎么样。
    起卦帮同学看工作,应了。
    2012年10月17日帮朋友算得第一卦
    2013年1月13日帮朋友测的第二卦,有些地方没看出来
    bzoj2588 Spoj 10628. Count on a tree
  • 原文地址:https://www.cnblogs.com/Quincy/p/4936859.html
Copyright © 2011-2022 走看看