muduo_base 源码分析:AtomicIntegerT
为什么需要原子性操作?
所谓原子操作是指不会被线程调度机制打断的操作;这种操作一旦开始,就一直运行到结束,中间不会有任何context switch (切换到另一个线程)。
以前见过一道面试题:i++ 是否为原子操作?
答案是i++ 不是原子操作,i++ 操作可分为三步,(1)从内存中读i 的值到寄存器,(2)对寄存器值+1,(3)把新的 i 的值写入内存。其中任何一步都有可能被多个线程干扰。
time Thread 1 Thread 2
0 loadeax, i
1 loadeax, i
2 addeax, 1
3 addeax, 1
4 storei, eax
5 storei, eax
在多线程环境下要让i++的结果只 +1,可使用锁(lock(); i++;unlock();),但是锁是服务器性能的四大杀手之一,系统开销较大。
gcc中提供的原子性操作
// 原子自增操作
type__sync_fetch_and_add (type *ptr, type value)
// 原子比较和交换(设置)操作
type __sync_val_compare_and_swap (type*ptr, type oldval type newval)
bool __sync_bool_compare_and_swap (type*ptr, type oldval type newval)
// 原子赋值操作
type __sync_lock_test_and_set (type *ptr,type value)
使用这些原子性操作,编译的时候需要加-march=cpu-type(可选择native,编译器自动识别CPU 类型)
AtomicIntegerT类图
AtomicIntegerT代码
//muduo/muduo/base/Atomic.h
template<typename T>
class AtomicIntegerT : boost::noncopyable
{
public:
AtomicIntegerT()
: value_(0)
{
}
T get() //获取value_
{
// in gcc >= 4.7: __atomic_load_n(&value_, __ATOMIC_SEQ_CST)
return __sync_val_compare_and_swap(&value_, 0, 0);
}
T getAndAdd(T x) //先获取value_ 后 +x
{
// in gcc >= 4.7: __atomic_fetch_add(&value_, x, __ATOMIC_SEQ_CST)
return __sync_fetch_and_add(&value_, x);
}
T addAndGet(T x) //先给value_ +x 后获取
{
return getAndAdd(x) + x;
}
T incrementAndGet() //给value_ +1并返回
{
return addAndGet(1);
}
T decrementAndGet() //给value_ -1并返回
{
return addAndGet(-1);
}
void add(T x) //给value_ +x
{
getAndAdd(x);
}
void increment() //给value_ +1
{
incrementAndGet();
}
void decrement() //给value_ -1
{
decrementAndGet();
}
T getAndSet(T newValue)//先获取value_值,再将value_设置为newValue
{
// in gcc >= 4.7: __atomic_store_n(&value, newValue, __ATOMIC_SEQ_CST)
return __sync_lock_test_and_set(&value_, newValue);
}
private:
volatile T value_;
};
volatile 作用
volatile作为指令关键字,确保本条指令不会因编译器的优化而省略,且要求每次直接读值。简单地说就是防止编译器对代码进行优化
当要求使用volatile 声明的变量的值的时候,系统总是重新从它所在的内存读取数据,而不是使用保存在寄存器中的备份。即使它前面的指令刚刚从该处读取过数据。而且读取的数据立刻被保存。
详解见:http://www.zhihu.com/question/31459750