原子操作和原子类型
原子操作和原子类型
原子操作是不可分割的操作,任何线程不会观察到操作“中间态”。如果读取操作是原子的,那么读取到的值要么是初始值,要么是某次完整修改后的值。
非原子操作可能出现“半完成”状态,导致数据竞争和未定义行为。
标准原子类型
- 标准原子类型定义在
<atomic>中,操作都是原子的。 is_lock_free()成员函数用于查询该原子类型是否使用无锁实现:true表示无锁,直接用硬件原子指令实现;false表示内部可能用互斥锁实现。
- C++17引入
is_always_lock_free静态常量,编译期表示是否无锁。 - 相关宏(如
ATOMIC_INT_LOCK_FREE)表示平台上该原子类型是否无锁:- 0:有锁实现;
- 1:运行时判断是否无锁;
- 2:无锁实现。
std::atomic_flag
-
唯一保证无锁的原子类型;
-
只能初始化为“清除”状态,使用宏
ATOMIC_FLAG_INIT初始化; -
提供两个操作:
clear(memory_order):清除标志;test_and_set(memory_order):设置标志并返回之前的值。
-
用于实现自旋锁:
class spinlock_mutex
{
std::atomic_flag flag;
public:
spinlock_mutex() : flag(ATOMIC_FLAG_INIT) {}
void lock() {
while(flag.test_and_set(std::memory_order_acquire));
}
void unlock() {
flag.clear(std::memory_order_release);
}
};
std::atomic<bool>
- 比
atomic_flag功能更全; - 可以用非原子bool构造和赋值:
std::atomic<bool> b(true);
b = false;
- 提供操作:
load(memory_order)store(value, memory_order)exchange(new_value, memory_order)compare_exchange_weak(expected, desired, success_order, failure_order)compare_exchange_strong(expected, desired, success_order, failure_order)
compare_exchange_weak可能出现伪失败(spurious failure),通常与循环配合使用:
bool expected = false;
while (!b.compare_exchange_weak(expected, true) && !expected) {
// 循环直到成功或预期值不符
}
compare_exchange_strong无伪失败,直接返回是否成功。
std::atomic<T*>(原子指针)
支持指针的原子操作:
load(),store(),exchange(),compare_exchange_weak(),compare_exchange_strong();- 指针加减操作:
fetch_add(),fetch_sub(),支持+=,-=,++,--;
示例:
class Foo {};
Foo arr[5];
std::atomic<Foo*> p(arr);
Foo* x = p.fetch_add(2); // p 指向 arr[2],返回原值 arr
assert(x == arr);
assert(p.load() == &arr[2]);
x = (p -= 1); // p 指向 arr[1],返回 arr[1]
assert(x == &arr[1]);
assert(p.load() == &arr[1]);
标准原子整型操作
除了 load(), store(), exchange(), compare_exchange_weak() 和 compare_exchange_strong() 外,还支持:
fetch_add(),fetch_sub()fetch_and(),fetch_or(),fetch_xor()- 复合赋值:
+=,-=,&=,|=,^= - 自增自减:
++,--
std::atomic<> 类模板(用户自定义类型)
- 可以使用自定义类型
T实例化std::atomic<T>,前提:- 类型
T有编译器生成的拷贝赋值运算符; - 无虚函数、无虚基类;
- 所有成员均支持拷贝赋值。
- 类型
- 其实现通常是基于内置类型的字节比较(memcmp),并使用内部锁保护原子操作(大多数平台不支持无锁自定义类型)。
- 可能的操作只限于
load(),store(),exchange(),compare_exchange_weak(),compare_exchange_strong()。 - 不支持复杂操作或其他运算符重载。
- 例子:不支持
std::atomic<std::vector<int>>,因为其操作复杂。 - 浮点类型虽然可以使用,但比较操作可能因不同表示(如NaN等)出现意外行为。
每一个原子类型所能使用的操作:

原子操作的非成员函数
- C语言兼容的接口,如
std::atomic_load(&a),std::atomic_store(&a, val)等。 - 这些非成员函数对应成员函数,增加了兼容性。
std::atomic_compare_exchange_weak()和std::atomic_compare_exchange_strong()参数不同,第一个参数是指针,第二个是预期值指针。std::atomic_flag的非成员函数如std::atomic_flag_test_and_set()等也支持显式内存序。
针对智能指针的原子操作
- C++ 标准库允许对
std::shared_ptr<>进行原子操作:- 不是原子类型,但提供
std::atomic_load(),std::atomic_store(),std::atomic_exchange(),std::atomic_compare_exchange()等。
- 不是原子类型,但提供
- 这打破了“只有原子类型才有原子操作”的原则,但保证了
shared_ptr线程安全。
示例:
std::shared_ptr<my_data> p;
void process_global_data() {
std::shared_ptr<my_data> local = std::atomic_load(&p);
process_data(local);
}
void update_global_data() {
std::shared_ptr<my_data> local(new my_data);
std::atomic_store(&p, local);
}
小结
- 原子操作避免了数据竞争和未定义行为。
- 标准库提供了多种原子类型,满足基本整型、指针、布尔和用户自定义类型的需求。
std::atomic_flag适合实现简单的自旋锁。std::atomic<bool>提供布尔原子操作,支持比较交换等。std::atomic<T*>支持指针原子操作和指针算术。- 对于用户自定义类型,原子支持有限,通常基于内部锁。
- 原子操作支持不同内存序,下一节5.3详述内存序的影响。
- 标准库还提供了与原子操作兼容的非成员函数,支持C语言兼容接口。
- 智能指针也有对应的原子操作,保证线程安全访问。

浙公网安备 33010602011771号