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

女爷i 2022-08-05 15:17 316阅读 0赞

“最轻量级的锁”,通常也叫”原子操作”,之所以加引号是因为他们在汇编级别并不是原子操作,是用多条指令完成的,这些操作大多都是利用CPU支持的汇编指令.在某些构架过时的CPU体系结构上,它们应该是用比较重量级的线程内锁实现的吧(我的猜测).

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

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体系结构的问题)

“原子操作”函数:

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

比较后交换

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

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

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

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

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

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

#include

template

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使用

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/2014/12/23/98965.html

发表评论

表情:
评论列表 (有 0 条评论,316人围观)

还没有评论,来说两句吧...

相关阅读

    相关 原子操作实现

    原子操作实现 关于CAS等原子操作:          在开始说无锁队列之前,我们需要知道一个很重要的技术就是CAS操作——Compare & Set,或是 C

    相关 原子操作

     原子操作 在多线程编程中,访问同一个变量时需要利用锁来限制同一个时刻只有一个线程访问,否则会遇到未知的错误,最常用的锁有信号量、线程锁。系统也提供了一些原子操作来实现

    相关 原子操作

    原子操作指的是在执行过程中不会被别的代码中断的操作 位和整型变量原子操作依赖底层CPU的原子操作来实现,因此所有这些函数都与CPU架构密切相关 整型原子操作 定义