线程和原子操作
线程和原子操作
线程thread基本使用
动或按值复制线程函数的参数。如果需要传递引用参数给线程函数,那么必须包装它(例如用 std::ref 或 std::cref)。
忽略来自函数的任何返回值。如果函数抛出异常,那么就会调用 std::terminate。需要将返回值或异常传递回调用方线程时可以使用 std::promise 或 std::async。
传值和传引用:
1 | void func1(int a , int b) { |
std::ref
用于包装按引用传递的值。std::cref
用于包装按const
引用传递的值。
创建一个可运行(创建时传入线程函数)的线程对象后,必须对该线程对象进行处理,要么调用join(),要么调用detach(),否则线程对象析构时程序将直接退出。
std::thread::~thread
销毁 thread 对象。若 *this 拥有关联线程( joinable() == true ),则调用std::terminate。
1 | void f(int a) { cout << a << endl;} |
如上代码将收到Abort
信号,因为,线程对象t1在作用域中没有调用join或detach,当t1出作用域时,将会抛出异常导致程序退出
线程遇到重载函数
1 | void f(int a) { std:: cout << a << std::endl; } |
原子变量
每个 std::atomic
模板的实例化和全特化定义一个原子类型。如果一个线程写入原子对象,同时另一线程从它读取,那么行为良好定义,std::atomic
既不可复制也不可移动。
std::atomic
模板可用于任何满足可复制构造,可复制赋值,可平凡复制类型T的特化,不支持复制初始化
1 | std::is_trivially_copyable<T>::value |
上述值为false
非良构
1 | std::atomic<int> foo(0); //直接初始化 |
- std::atomic
::store
void store(T desired , std::memort_order order = std::memory_order_seq_cst) noexcept
以 desired 原子地替换当前值。按照 order 的值影响内存。order必须是 std::memory_order_relaxed . std::memory_order_release , std::memory_order_seq_cst
- std::atomic
::load
void load(std::memort_order order = std::memory_order_seq_cst) const noexcept
,原子地加载并返回原子变量的当前值。按照 order 的值影响内存。返回原子变量的当前值。
- std::atomic
::operator=
T operator=( T desired ) noexcept;
将 desired 原子地赋给原子变量。等价于 store(desired)。返回 desired
std::call_once std::once_flag
定义:
1 | template< class Callable, class... Args > |
如果在调用
std::call_once
的时刻,flag 指示 f 已经调用过,那么std::call_once
会立即返回(称这种对std::call_once
的调用为消极)。否则,std::call_once 会调用 INVOKE(std::forward
(f), std::forward (args)…)。与 std::thread 的构造函数或 std::async 不同,不会移动或复制参数,因为不需要转移它们到另一执行线程(称这种对 std::call_once 的调用为积极)。 - 如果该调用抛出了异常,那么将异常传播给
std::call_once
的调用方,并且不翻转 flag,这样还可以尝试后续调用(称这种对std::call_once
的调用为异常)。 - 如果该调用正常返回(称这种对
std::call_once
的调用为返回),那么翻转 flag,并保证以同一 flag 对std::call_once
的其他调用为消极。
- 如果该调用抛出了异常,那么将异常传播给
1 |
|
异步操作
future
1 | template< class T > class future; |
类模板 std::future
提供访问异步操作结果的机制
通过std::async std::packaged_task std::promise
创建的异步操作能提供一个std::future
对象给该异步操作的创建者,然后,异步操作的创建者能用各种方法查询 等待 或从std::future
中提取值。若异步操作认为提供值,则这些方法可能阻塞。异步操作准备好发送结果给创建者时,它能通过修改链接到创建者的 std::future
的共享状态,std::future
所引用的共享状态不与另一异步返回对象共享
- get():
get
方法等待直至future
拥有合法结果并(依赖于使用哪个模板)获取它。它等效地调用 wait() 等待结果,泛型模板和二个模板特化各含单个get
版本。get
的三个版本仅在返回类型有别。若调用此函数前 valid() 为 false 则行为未定义。
async
1 | template< class Function, class... Args > |
函数模板 std::async
异步地运行函数 f(有可能在可能是线程池一部分的分离线程中),并返回最终将保有该函数调用结果的std::future
1 | int find_result_to_add() { |
packaged_task
1 | template< class R, class ...Args > |
类模板 std::packaged_task 包装任何可调用 (Callable) 目标(函数、 lambda 表达式、 bind 表达式或其他函数对象),使得能异步调用它。其返回值或所抛异常被存储于能通过 std::future 对象访问的共享状态中。
std::function
std::packaged_task
是多态、具分配器的容器:可在堆上或以提供的分配器分配存储的可调用对象。
operator():如果以
INVOKE<R>(f ,args...)
调用存储的任务 f。任务返回值或任何抛出的异常被存储于共享状态。令共享状态就绪,并解除阻塞任何等待此操作的线程。get_future():返回与 *this 共享同一共享状态的
future
。get_future
只能对每个packaged_task
调用一次。reset():重置状态,抛弃先前执行的结果。构造共享状态。等价于 *this = packaged_task(std::move(f)) ,其中
f
是存储的任务。
1 | int add(int a, int b, int c) { |
promise
传统的线程返回值:传递一个指针给线程,表示该线程将会把返回值写入指针指向的内存空间。此时主线程将用条件变量等待值被写入,当线程把值写入指针指定的内存后,将唤醒(signal)条件变量,然后主线程将被唤醒,然后从指针指向的内存中获取返回值。
为了实现获取一个返回值的需求,使用传统的方法,我们需要条件变量(condition variable), 互斥量(mutex),和指针三个对象。
C++11的方法:使用std::future和std::promise
1 | template< class R > class promise; |
类模板 std::promise
提供存储值或异常的设施,之后通过 std::promise
对象所创建的 std::future
对象异步获得结果。注意 std::promise
只应当使用一次。
std::future
get_future:返回与 *this 关联同一状态的 future 对象。若 *this 无共享状态,或已调用 get_future
则抛出异常set_value(const R& value):原子地存储
value
到共享状态,并令状态就绪。
1 | void print(std::promise<std::string>& p) |