thread
thread是C++11引入的一个处理并发的低层接口。
1、操作
操作 | 作用 |
---|---|
thread t | 默认构造函数,创建一个没有关联线程(nonjoinable)的thread对象 |
thread t(f, …) | 创建一个关联到线程(joinable)的thread对象。这是一个可变参模板函数,f 是线程函数,f后面的函数参数是f的传参;或抛出std::system_error |
thread t(rv) | 移动构造函数,取rv的状态并使rv变成nonjoinable |
~thread() | 析构函数,如果thread对象是joinable,则调用std::terminate() |
t = rv | 移动赋值运算符,如果rv是joinable则调用std::terminate() |
t.joinable() | 判断thread对象是否关联到线程 |
t.join() | 等待关联线程完成工作,然后使t变成nonjoinable。如果t不是joinable则抛出std::system_error |
t.detach() | 让线程在后台运行,并使t变成nonjoinable。如果t不是joinable则抛出std::system_error |
t.get_id() | 如果t是joinable,则返回一个唯一的thread id;否则返回std::thread::id() |
2、细节1
线程函数如果发生异常并未被捕获,程序会被终止并调用std::terminate()。
3、细节2
对于joinable thread,必须调用join()或detach(),或者通过移动构造、移动赋值使它成为nonjoinable,否则在thread对象生命期结束时程序会终止并调用std::terminate()。需要注意的是,在创建一个关联到线程的thread对象时线程就开始执行,而不是调用join或detach时才执行。
4、关于detached thread
需要注意的是,调用detach()运行在后台的线程不应该访问生命已结束的变量,特别是global和static变量。网上查找的资料(没有找到更权威的描述)指出,main()结束或者调用exit(),global或static变量会在子线程被终止前析构。也就是说,detached thread有可能访问到已被销毁或正在析构的global或static变量。
所以,使用detached thread时,应该:
1、detached thread 应尽可能只访问local copy。
2、detached thread 需要访问global或static变量时,应使用同步手段确保global或static变量在detached thread结束前不被销毁;或者以调用quick_exit()的方式结束程序,这个函数可以在程序结束时,不销毁global和static变量。
promise
promise对象可以用来存放一个值或者异常(称作shared state),但不能同时存放值和异常,存储了一个值或异常的promise是ready的。利用promise对象可以在线程之间传递参数、结果和异常。
1、支持的操作
操作 | 作用 |
---|---|
promise p | 默认构造函数,绑定shared state |
promise p(allocator_arg, alloc) | 指定分配器构造promise对象 |
promise p(rv) | 移动构造函数 |
~promise() | 析构函数,释放shared state,如果它不是ready,就存储一个future_error异常,错误码为broken_promise |
p = rv | 移动赋值操作符 |
swap(p1, p2) | 交换p1、p2状态 |
p1.swap(p2) | 交换p1、p2状态 |
p.get_future() | 创建一个用于取出shared state的future对象 |
p.set_value(val) | 设val为值并令状态为ready |
p.set_value_at_thread_exit(val) | 在当前线程结束时设val为值并令状态为ready |
p.set_exception(e) | 设e为异常并令状态为ready |
p.set_exception_at_thread_exit(e) | 在当前线程结束时设e为异常并令状态为ready |
2、异常
没有绑定shared state的promise对象调用get_future(),会抛出std::future_error异常,错误码是std::future_errc::no_state。
promise对象第只能调用一次get_future(),否则会抛出std::future_error异常,错误码是std::future_errc::future_already_retrieved。
promise对象只能调用一次set函数,否则会抛出std::future_error异常,错误码是std::future_errc::promise_already_satisfied。
try
{
std::promise<int> p;
std::promise<int> p1(std::move(p)); // 移动构造函数移走p的shared state,
//p.get_future(); // 抛出future_error异常,错误码是no_state
p1.set_value(10);
//p1.set_value(10); // 抛出future_error异常,错误码是promise_already_satisfied。
p1.get_future();
//p1.get_future(); // 抛出future_error异常,错误码是future_already_retrieved
}
catch (std::exception &e)
{
std::cout << e.what() << std::endl;
}
3、set_xxxx函数
set_xxxx系列成员函数都是线程安全的。
packaged_task
packaged_task<>用来封装可调用对象及其结果(shared state)。类似function<>,但function<>不能封装结果。
1、支持操作
操作 | 效果 |
---|---|
packaged_task pt | 默认构造函数,packaged_task对象没有绑定shared state和可调用对象 |
packaged_task pt(f) | 构造函数 |
packaged_task pt(alloc, f) | 构造函数,指定分配器 |
packaged_task pt(rv) | 移动构造函数 |
~packaged_task() | 析构函数,释放shared state,且使shared state变成ready |
pt = rv | 移动赋值操作符 |
swap(pt1, pt2) | 交换函数 |
pt1.swap(pt2) | 交换函数 |
pt.valid() | pt拥有shared state时返回true |
pt.get_future() | 返回一个future对象,用于读取shared state |
pt(args) | 调用其封装的可调用对象,并使shared state变成ready |
pt.make_ready_at_thread_exit(args) | 调用其封装的可调用对象,并在线程退出时使shared state变成ready |
pt.reset() | 释放旧的shared state,使其变成ready,并建立一个新的shared state |
2、异常
packaged_task对象没有绑定shared state时,调用绑定的可调用对象或者get_future会抛出std::future_error异常,错误码是std::future_errc::no_state。
第二次调用get_future会抛出std::future_error异常,错误码是std::future_errc::future_already_retrieved。
第二次调用可调用对象会抛出std::future_error异常,错误码是std::future_errc::promise_already_satisfied.
try
{
std::packaged_task<int()> pt;
pt.get_future(); // 抛出future_error异常,错误码no_state
pt(); // 抛出future_error异常,错误码no_state
std::packaged_task<int()> pt(std::bind(add, 1, 2));
pt.get_future();
pt.get_future(); // 抛出future_error异常,错误码future_already_retrieved
pt();
pt(); // 抛出future_error异常,错误码promise_already_satisfied
}
catch (std::exception &e)
{
std::cout << e.what() << std::endl;
}
3、reset和析构
packaged task的析构函数和reset函数会释放shared state,并且如果shared state不是ready就令它变成ready,然后把一个std::future_error异常并夹带差错码std::future_errc::broken_promise存储在shared state中。