[转]用汇编实现原子操作

原子操作(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

时间: 2024-10-01 13:06:10

[转]用汇编实现原子操作的相关文章

C++拾遗--多线程:原子操作解决线程冲突

C++拾遗--多线程:原子操作解决线程冲突 前言 在多线程中操作全局变量一般都会引起线程冲突,为了解决线程冲突,引入原子操作. 正文 1.线程冲突 #include <stdio.h> #include <stdlib.h> #include <process.h> #include <Windows.h> int g_count = 0; void count(void *p) { Sleep(100); //do some work //每个线程把g_c

多线程编程之原子操作

在多线程环境中,对共享的变量的访问,可以使用基于Compare And Swap这种lock free的技术进行实现,这种实现的好处是效率高. 一.原子操作摘录 1.1 Android 源码:system/core/libcutils /atomic.c(针对X86): 1 #elif defined(__i386__) || defined(__x86_64__) 2 3 void android_atomic_write(int32_t value, volatile int32_t* ad

再探c++11 Thread库之原子操作

我在之前一篇博文<初探c++11 Thread库之使写多线程程序>中,着重介绍了<thread>头文件中的std::thread类以及其上的一些基本操作,至此我们动手写多线程程序已经基本没有问题了.但是,单线程的那些"坑"我们仍还不知道怎么去避免. 多线程存在的问题 多线程最主要的问题就是共享数据带来的问题.如果共享数据都是只读的,那么没问题,因为只读操作不会影响到数据,更不会涉及对数据的修改,所以所有线程都会获得同样的数据.但是,当一个或多个线程要修改共享数据

原子操作&amp;优化和内存屏障

原子操作 假定运行在两个CPU上的两个内核控制路径试图执行非原子操作同时"读-修改-写"同一存储器单元.首先,两个CPU都试图读同一单元,但是存储器仲裁器插手,只允许其中的一个访问而让另一个延迟.然而,当第一个读操作已经完成后,延迟的CPU从那个存储器单元正好读到同一个(旧)值.然后,两个CPU都试图向那个存储器单元写一新值,总线存储器访问再一次被存储器仲裁器串行化,最终,两个写操作都成功.但是,全局的结果是不对的,因为两个CPU写入同一(新)值.因此,两个交错的"读-修改-

[OS] 多线程--原子操作 Interlocked系列函数

转自:http://blog.csdn.net/morewindows/article/details/7429155 上一篇<多线程--第一次亲密接触 CreateThread与_beginthreadex本质区别>中讲到一个多线程报数功能.为了描述方便和代码简洁起见,我们可以只输出最后的报数结果来观察程序是否运行出错.这也非常类似于统计一个网站每天有多少用户登录,每个用户登录用一个线程模拟,线程运行时会将一个表示计数的变量递增.程序在最后输出计数的值表示有今天多少个用户登录,如果这个值不等

原子操作 Interlocked系列函数

上一篇<多线程第一次亲密接触 CreateThread与_beginthreadex本质区别>中讲到一个多线程报数功能.为了描述方便和代码简洁起见,我们可以只输出最后的报数结果来观察程序是否运行出错.这也非常类似于统计一个网站每天有多少用户登录,每个用户登录用一个线程模拟,线程运行时会将一个表示计数的变量递增.程序在最后输出计数的值表示有今天多少个用户登录,如果这个值不等于我们启动的线程个数,那显然说明这个程序是有问题的.整个程序代码如下: [cpp] view plain copy #inc

秒杀多线程第三篇 原子操作 Interlocked系列函数

版权声明:本文为博主原创文章,未经博主允许不得转载. 上一篇<多线程第一次亲密接触 CreateThread与_beginthreadex本质区别>中讲到一个多线程报数功能.为了描述方便和代码简洁起见,我们可以只输出最后的报数结果来观察程序是否运行出错.这也非常类似于统计一个网站每天有多少用户登录,每个用户登录用一个线程模拟,线程运行时会将一个表示计数的变量递增.程序在最后输出计数的值表示有今天多少个用户登录,如果这个值不等于我们启动的线程个数,那显然说明这个程序是有问题的.整个程序代码如下:

无锁-CAS原子操作

CAS原子操作--Compare & Set,或是 Compare & Swap,现在几乎所有的CPU指令都支持CAS的原子操作,X86下对应的是 CMPXCHG 汇编指令. 大家应该还记得操作系统里面关于"原子操作"的概念,一个操作是原子的(atomic),如果这个操作所处的层(layer)的更高层不能发现其内部实现与结构.原子操作可以是一个步骤,也可以是多个操作步骤,但是其顺序是不可以被打乱,或者切割掉只执行部分.有了这个原子操作这个保证我们就可以实现无锁了. 相对

原子操作

今天看到文章讨论 i++ 是不是原子操作. 答案是不是! 参考:http://blog.csdn.net/yeyuangen/article/details/19612795 1.i++ 不是,分为三个阶段: 内存到寄存器寄存器自增写回内存这三个阶段中间都可以被中断分离开. 2.++i首先要看编译器是怎么编译的, 某些编译器比如VC在非优化版本中会编译为以下汇编代码: __asm{        moveax,  dword ptr[i]        inc eax        mov dw