muduo库-原子类AtomicIntegerT<T>

muduo库-原子类AtomicIntegerT

C++中的原子操作有两种实现方式:

  1. C++11以后,提供st::atomic可以实现T类型数据的原子操作,主要包括:初始化、读取值、写值、自增自减(i.e. 前置或后置++)等。
  2. C++11以前的版本,需要用GCC编译器提供的原子操作接口,实现原子操作。

AtomicIntegerT模板类

muduo产生与C++11流行之前,因此用了第二种方案。实际上,Linux下面,C++11中的原子操作实现,也是用的第一种方案实现的。

自定义AtomicIntegerT模板:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
/* 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包装int32_t, int64_t,然后重定义,我们在.h文件中声明这2个

1
2
3
// namespace muduo::detail
typedef detail::AtomicIntegerT<int32_t> AtomicInt32;
typedef detail::AtomicIntegerT<int64_t> AtomicInt64;

单元测试

主要针对AtomicInt64、AtomicInt32 这2个常用的、具体的类型,进行测试。

方法:通过构造实例对象后,调用成员函数get()、getAndAdd()、addAndGet()、incrementAndGet()、decrementAndGet(),对原子对象进行修改,然后根据返回值判断值是否为预期值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 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原子操作

GCC提供了一套原子操作的接口,可以实现原子操作。这些接口都是以__sync开头的,如__sync_fetch_and_add(),__sync_val_compare_and_swap()等。

原子自增操作:

1
type __sync_fetch_and_add(type *ptr, type value)

原子赋值操作:

1
type __sync_lock_test_and_set(type *ptr, type value)

使用这些原子操作时,编译时需要加-march=cpu-type(CPU体系结构=CPU类型,可以指定为native)

volatile关键字

确保本条指令不会因为编译器的优化而省略,而且要求每次从内存直接读值,而不是读高速cache中的备份。

1
volatile T value_;

本文参考自:muduo笔记 原子类AtomicIntegerT


muduo库-原子类AtomicIntegerT<T>
https://gstarmin.github.io/2023/06/21/muduo库-原子类AtomicIntegerT-T/
作者
Starmin
发布于
2023年6月21日
许可协议