原子操作
C++中的原子操作:
1)C++11以后,提供st::atomic
2)对于C++11以前的版本,需要用GCC编译器提供的原子操作接口,实现原子操作。
AtomicIntegerT模板类
muduo产生与C++11流行之前,因此用了第二种方案。实际上,Linux下面,C++11中的std::stomic实现,也是用的第一种方案实现的。
自定义AtomicIntegerT
/* GCC atomic operation see
* https://www.cnblogs.com/the-tops/p/6347584.html
* https://gcc.gnu.org/wiki/Atomic/GCCMM/AtomicSync
*/
template<typename T>
class AtomicIntegerT : noncopyable
{
public:
AtomicIntegerT()
:value_(0)
{ }
T get()
{
// CAS(compare and swap)
// in gcc >= 4.7: __atomic_load_n(&value, __ATOMIC_SEQ_CST)
return __sync_val_compare_and_swap(&value_, 0, 0);
}
// 先fetch获取值(value_),然后再add加x
T getAndAdd(T x)
{
// in gcc >= 4.7: __atomic_fetch_add(&value_, x, __ATOMIC_SEQ_CST)
return __sync_fetch_and_add(&value_, x);
}
// getAndAdd()中已经用原子操作改变了value_值,这里是通过值传递方式返回 old value_ + x
T addAndGet(T x)
{
return getAndAdd(x) + x;
}
// 前缀式递增,相当于++value_
T incrementAndGet()
{
return addAndGet(1);
}
// 前缀式递减,相当于--value_
T decrementAndGet()
{
return addAndGet(-1);
}
// 求和,相当于value_ = value_ + x
void add(T x)
{
getAndAdd(x);
}
// 单纯递增,不关心返回值
void increment()
{
incrementAndGet();
}
// 单纯递减,不关心返回值
void decrement()
{
decrementAndGet();
}
// 先fetch old value_,然后set value_ = newValue
T getAndSet(T newValue)
{
// in gcc >= 4.7: __atomic_exchange_n(&value_, newValue, __ATOMIC_SEQ_CST)
return __sync_lock_test_and_set(&value_, newValue);
}
private:
volatile T value_;
};
为了使用方便,同时避免重复命名、实例化、甚至编译,使用AtomicIntegerT
// namespace muduo::detail
typedef detail::AtomicIntegerT<int32_t> AtomicInt32;
typedef detail::AtomicIntegerT<int64_t> AtomicInt64;
单元测试
主要针对AtomicInt64、AtomicInt32 这2个常用的、具体的类型,进行测试。
方法是通过构造实例对象后,调用成员函数get()、getAndAdd()、addAndGet()、incrementAndGet()、decrementAndGet(),对原子对象进行修改,然后根据返回值判断值是否为预期值。
// AtomicInt64 的测试
{
AtomicInt64 a0;
assert(a0.get() == 0);
assert(a0.getAndAdd(1) == 0);
assert(a0.get() == 1);
assert(a0.addAndGet(2) == 3);
assert(a0.get() == 3);
assert(a0.incrementAndGet() == 4);
assert(a0.get() == 4);
a0.increment();
assert(a0.get() == 5);
assert(a0.addAndGet(-3) == 2);
assert(a0.getAndSet(100) == 2);
assert(a0.get() == 100);
}
// AtomicInt32 的测试类同,只需要修改a0的类型为AtomicInt32即可,具体代码略
...
知识点
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 (CPU体系结构=CPU类型,可以指定为native)
volatile关键字
确保本条指令不会因为编译器的优化而省略,而且要求每次从内存直接读值,而不是读高速cache中的备份。
volatile T value_;
参考
https://www.cnblogs.com/the-tops/p/6347584.html
https://gcc.gnu.org/wiki/Atomic/GCCMM/AtomicSync
https://coolshell.cn/articles/8239.html