C++学习资料下载:
原子操作
原子操作又称为原子性操作,是多线程环境中的一种同步机制。这种机制保证在操作过程中不会被线程的调度机制所打断,从而避免多个线程同时对一个数据进行操作产生的数据竞争和数据不一致的问题。
std::atomic
C++11提供了std::atomic模板,实例化一个std::atomic对象,使得我们对一个原子类型操作由一组指令,最小化到一个CPU。
std::atomic成员函数
- load() 读操作
- store()写操作
- compare_exchange_weak/compare_exchange_strong CAS操作
compare_exchange_strong
当前值与期望值(expect)相等时,修改当前值为设定值(desired),返回true
当前值与期望值(expect)不等时,将期望值(expect)修改为当前值,返回false
weak版和strong版的区别:
weak版本的CAS允许偶然出乎意料的返回(比如在字段值和期待值一样的时候却返回了false,并且没有将字段值设置成desire的值),不过在一些循环算法中,这是可以接受的。通常它比起strong有更高的性能。
-
is_lock_free 用来检查该原子类型是否需支持原子操作
CAS:compare and set,是一条cpu原子指令。
对于整形还提供一些特殊的成员函数
- fetch_add() 原子加
- fetch_sub() 原子减
- fetch_and() 原子与
- fetch_or() 原子或
std::atomic_flag
最简单的atomic类型,该对象可以在两个状态之间切换,设置和清除;
对象必须被ATOMIC_FLAG_INIT初始化;
std::atomic_flag flag = ATOMIC_FLAG_INIT;
初始化的对象只能做三件事:销毁,清除、设置;
对应成员函数:
clear()
test_and_set()
c++原子操作的六种内存顺序
atomic原子操作可以使用memory_order来控制变量在不同线程的可见顺序的可见性,有以下六种内存顺序可选择
typedef enum memory_order {
memory_order_relaxed,
memory_order_consume,
memory_order_acquire,
memory_order_release,
memory_order_acq_rel,
memory_order_seq_cst
} memory_order;
- memory_order_release/memory_order_acquire
memory_order_release是写操作store函数,表示该操作之前的任何写操作都不能放到该操作之后,即写语句不能调到本语句之后;
memory_order_acquire是读操作load函数,表示该读操作之后的的任何读内存操作都不放到该操作之前
对于同一个原子变量,release操作之前的写操作,一定对随后的acquire操作后的读可见,这两种内存序一般需要配对使用
- memory_order_release/memory_order_consume
此搭配同上一个基本一样,带式该组合是一种更为宽松的内存序可见情况,comsume只是阻止对该院系变量有依赖的操作重排到前面去,而非所有读操作
- memory_order_acq_rel
此内存序是release和acquire的结合,包含这两种的特性,因此任何读写操作的重排都不能跨越这个调用
- memory_order_seq_cst
默认内存序选项,是最严格的内存顺序,前面的语句不能挑到后面,后面的语句不能调到前面
- memory_order_relaxed
只保证当前语句是原子操作,对你内存顺序不做任何保证
std::atomic详解
一致性模型
1. 线性一致性:
2. 顺序一致性
3. 因果一致性
4. 最终一致性
内存顺序
1. 宽松模型
std::atomic<int> counter = {0};
std::vector<std::thread> vt;
for (int i = 0; i < 100; ++i)
{
vt.emplace_back([&](){
counter.fetch_add(1, std::memory_order_relaxed);
});
}
for (auto& t : vt)
{
t.join();
}
std::cout << "current counter:" << counter << std::endl;
2. 释放/消费模型
// 初始化为 nullptr 防止 consumer 线程从野指针进行读取
std::atomic<int*> ptr(nullptr);
int v;
std::thread producer([&]() {
int* p = new int(42);
v = 1024;
ptr.store(p, std::memory_order_release);
});
std::thread consumer([&]() {
int* p;
while(!(p = ptr.load(std::memory_order_consume)));
std::cout << "p: " << *p << std::endl;
std::cout << "v: " << v << std::endl;
});
producer.join();
consumer.join();
3. 释放/获取模型
std::vector<int> v;
std::atomic<int> flag = {0};
std::thread release([&]() {
v.push_back(42);
flag.store(1, std::memory_order_release);
});
std::thread acqrel([&]() {
int expected = 1; // must before compare_exchange_strong
while(!flag.compare_exchange_strong(expected, 2, std::memory_order_acq_rel)) {
expected = 1; // must after compare_exchange_strong
}
// flag has changed to 2
});
std::thread acquire([&]() {
while(flag.load(std::memory_order_acquire) < 2);
std::cout << v.at(0) << std::endl; // must be 42
});
release.join();
acqrel.join();
acquire.join()
4. 顺序一致模型
std::atomic<int> counter = {0};
std::vector<std::thread> vt;
for (int i = 0; i < 100; ++i)
{
vt.emplace_back([&](){
counter.fetch_add(1, std::memory_order_seq_cst);
});
}
for (auto& t : vt)
{
t.join();
}
std::cout << "current counter:" << counter << std::endl;
std::atomic应用
使用std::atomic<bool> 实现一个互斥锁。
#pragma once
#include <iostream>
#include <atomic>
class MyMutex
{
public:
MyMutex() = default;
~MyMutex() = default;
void lock()
{
bool expect = false;
while (!flag.compare_exchange_strong(expect, true, std::memory_order::memory_order_seq_cst));
}
void unlock()
{
flag.store(false);
}
private:
std::atomic<bool> flag = false;
};
代码供参考,欢迎大家指正