C++STL并发库
线程(def in <thread>)¶
- thread(c++11)
- 在析构时自动终止。
- join(阻塞等待线程完成),detach(分离线程)。
- 命名空间
this_thread:get_id,yield(重新调度),sleep_for,sleep_until。 - 不可复制,可以移动。
- jthread(c++20):相比于thread,jthread有额外的私有成员。
- 在析构时会自动join。
- 能够支持线程的中断(接收主线程的中断信息)。
原子操作(def in <atomic>)¶
原子类型¶
- atomic:布尔,整数,浮点数(c++20),指针。
- atomic_ref:非原子对象的原子操作。
| 原子类型上的操作 | 解释 | 版本 |
|---|---|---|
| atomic_is_lock_free | 检查是否免锁 | C++11 |
| atomic_store | 将非原子的值赋值给原子对象 | C++11 |
| atomic_load | 获取原子对象的值 | C++11 |
| atomic_exchange | 将非原子的值赋值给原子对象,返回原子对象的旧值。 | C++11 |
| atomic_compare_exchange_weak(*obj, *expected, desired) | 如果*obj==*expected,则obj=desired,否则*expected=*obj | C++11 |
| atomic_compare_exchange_strong | 相较于weak版本,weak版本可以容忍假性失败(实际相等,判断为不相等),strong版本不会出现这种情况。 | C++11 |
| atomic_fetch_add | 原子对象+=非原子对象,返回原子对象旧值(下同) | C++11 |
| atomic_fetch_sub | ||
| atomic_fetch_and | ||
| atomic_fetch_or | ||
| atomic_fetch_xor | ||
| atomic_wait(*obj, old) | 若*obj==old,则阻塞直到atomic_notify_one或atomic_notify_all且*obj!=old | C++20 |
原子标志类型¶
- atomic_flag:是一种原子布尔类型
- 与
atomic<bool>的区别:不提供store或load。
| 原子标志类型操作 | 解释 | 版本 |
|---|---|---|
| atomic_flag_clear | 设置为false | C++11 |
| atomic_flag_test_and_set | 设置为true且返回旧值 | C++11 |
| atomic_test | 返回值 | C++20 |
| atomic_flag_wait | 阻塞直到被唤醒且更改 | C++20 |
注意事项¶
- 所有操作函数都可以视为对应原子对象的方法。
atomic_xxx_explicit:所有操作函数都有explicit版本指定内存定序约束(memory_order)。
内存同步顺序¶
Todo
互斥(def in <mutex>)¶
互斥类型¶
- mutex
- timed_mutex:时间限定。
- recursive_mutex:可以被同一线程递归锁定。
- recursive_timed_mutex
通用互斥管理¶
| 通用互斥管理类型 | 解释 | 版本 |
|---|---|---|
| lock_guard | RAII风格,不可手动释放 | C++11 |
| scoped_lock | RAII风格,同时获取多个锁,防死锁 | C++17 |
| unique_lock | 可以手动释放 | C++11 |
C++
std::scoped_lock slk(m1,m2);
//等价lock_guard实现
std::lock(m1,m2);
std::lock_guard<std::mutex> lkg1(m1, std::adopt_lock);
std::lock_guard<std::mutex> lkg2(m2, std::adopt_lock);
//(不完全)等价unique_lock实现
std::unique_lock<std::mutex> ulk1(m1, std::defer_lock);
std::unique_lock<std::mutex> ulk2(m2, std::defer_lock);
std::lock(ulk1,ulk2);
- adopt_lock:假设调用时已经获取了锁(帮助锁在析构时释放)。
- defer_lock:不获取锁的所有权。
- try_to_lock:尝试获取锁的所有权但不阻塞。
| 通用锁定操作 | 解释 | 版本 |
|---|---|---|
| try_lock | 尝试对所有锁try_lock,一个失败则释放所有 | C++11 |
| lock | 对所有锁lock,失败则阻塞,防死锁 | C++11 |
单次调用¶
- call_once
共享互斥类型(def in shared_mutex)¶
- shared_mutex(C++17)
- shared_timed_mutex(C++14)
| 共享互斥管理类型 | 解释 | 版本 |
|---|---|---|
| shared_lock | 共享锁(读锁) | C++14 |
共享互斥类型的特点:
- 如果一个线程获取了独占锁,则其他线程无法获得该锁(包括独占和共享)。
- 如果没有线程获取了独占锁,则共享锁可以被多个线程获取。
- 一个线程同一时刻只能获取一个锁(独占或共享)。
简而言之:一个写锁,多个读锁。
条件变量(def in condition_variable)¶
条件变量应与互斥量一起使用:
stateDiagram-v2
A: lock(mutex)
AA: lock(mutex)
B: pred
C: unlock(mutex) & blocking
CC: unlock(mutex) & blocking
D: notified & lock(mutex)
DD: notified & lock(mutex)
E: do something
EE: do something
F: unlock(mutex)
FF: unlock(mutex)
state wait_with_pred {
state if <<choice>>
[*] --> A
A --> B
B --> if
if --> C: False
C --> D: wait for
D --> B
if --> E: True
E --> F
F --> [*]
}
state wait_without_pred {
[*] --> AA
AA --> CC
CC --> DD :wait for
DD --> EE
EE --> FF
FF --> [*]
}
故:使用条件变量的互斥量需要使用unique_lock(可以手动释放)
- condition_variable:只能与unique_lock关联
- condition_variable_any:与任何锁关联
| 条件变量操作 | 解释 | 版本 |
|---|---|---|
| wait(lk) | 阻塞,释放锁(右图) | C++11 |
| wait(lk, pred) | 唤醒后再判断(左图) | |
| wait_for | C++11 | |
| wait_until | C++11 | |
| notify_one | 唤醒一个 | C++11 |
| notify_all | 唤醒全部 | C++11 |
信号量(def in semaphore)¶
轻量同步原件,比条件变量更有效率。
- counting_semaphore:非负
- binary_semaphore:0-1
| 信号量操作 | 解释 | 版本 |
|---|---|---|
| release | 信号量+1,尝试唤醒阻塞的线程 | C++20 |
| acquire | 尝试-1,不能减少则阻塞 | C++20 |
| try_acquire | 尝试-1,不阻塞 | C++20 |
| try_acquire_for | C++20 | |
| try_acquire_until | C++20 |
锁存器(def in latch)与屏障(def in barrier)¶
- latch:只能(原子)减少的计数器,只能在创建时初始化值。
| 锁存器操作 | 解释 | 版本 |
|---|---|---|
| count_down | 计数器-1 | C++20 |
| try_wait | 计时器为0则返回true | C++20 |
| wait | 阻塞直到计数器为0 | C++20 |
| arrive_and_wait | 计数器-1并wait | C++20 |
- barrier:阶段同步点,构造时确定期待计数expect,函数f,当计数减少到0时恢复到expect(会被arrive_and_drop影响)并开始下一阶段。
| 屏障操作 | 解释 | 版本 |
|---|---|---|
| arrive | 计数器-1 | C++20 |
| wait | 阻塞,直到下一阶段 | C++20 |
| arrive_and_wait | 计数器-1并wait | C++20 |
| arrive_and_drop | 计数器-1且expect-1 | C++20 |
Future(def in Future)¶
Future用于获取异步任务的返回值(或异常)。
Future可以理解为一个未来会有值的变量(由async,promise,packaged_task产生)。
- async:异步任务
launch::async:新线程异步任务。
launch::dererred:首次调用结果时执行任务(惰性求值)。
位掩码枚举:可以使用launch::async|launch::dererred,同时传递两个策略。
- promise:传输一个值(异常)到future
C++
std::promise<int> p;
std::future<int> f2 = p.get_future();
std::thread( [&p]{ p.set_value_at_thread_exit(9); }).detach();
- packaged_task:封装可调用对象