引言
std::async、std::packaged_task 和 std::promise 是C++11引入的三个用于异步编程的工具,它们都与 std::future 配合使用,用于在多线程环境中执行任务并获取结果。本篇文中的工具都在<future>头文件中声明。
std::future
std::future 的核心功能
- 获取异步操作的结果:
- 通过
get() 方法获取异步操作的结果。
- 如果结果尚未准备好,
get() 会阻塞当前线程,直到结果可用。
- 检查异步操作的状态:
- 通过
valid() 方法检查 std::future 是否关联了一个有效的共享状态。
- 通过
wait()、wait_for() 和 wait_until() 方法等待异步操作完成。
- 移动语义:
std::future 只能移动(Move),不能复制。
std::future 的主要方法
| 方法 |
描述 |
get() |
获取异步操作的结果。如果结果未准备好,会阻塞当前线程直到结果可用。 |
valid() |
检查 std::future 是否关联了一个有效的共享状态。 |
wait() |
阻塞当前线程,直到异步操作完成。 |
wait_for() |
阻塞当前线程一段时间,等待异步操作完成。 |
wait_until() |
阻塞当前线程直到某个时间点,等待异步操作完成。 |
share() |
将 std::future 转换为 std::shared_future,允许多个线程共享结果。 |
get和wait方法
wait方法没有返回值,只是等待异步操作完成
get方法等待异步操作完成并会返回具体future<T>中T类型的值
调用wait方法后,还可以调用get方法;返过来则不行
std::async
- async是一个模板函数,下面是这个函数的使用方法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
int func1() {
std::cout << " (hello world in func1) " << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(5));
return 1024;
}
void func2() {
std::cout << " (hello world in func2) " << std::endl;
}
int main() {
//第一个参数的两种形式deferred async(可以省略)
std::future<int> f1 = std::async(std::launch::deferred, func1);
//如果第一个参数是deferrd,则func1不会立即执行,等到后面调用get()或wait()方法时才会执行。
//如果第一个参数是async,创建一个新线程立刻执行func1,并返回结果。(async可以省略)
//如果第一个参数是 deferred | async ,那么就会由async的实现自行决定选择运行方式
func2();
std::this_thread::sleep_for(std::chrono::seconds(5));
std::cout << " Result: " << f1.get() << std::endl; //等待func1执行完毕,并获取结果
}
|
std::package_task
std::packaged_task 的核心功能
- 包装可调用对象:
std::packaged_task 可以包装一个可调用对象(函数、lambda表达式、函数对象等)。
- 关联
std::future:
- 通过
get_future() 方法,可以获取一个与任务结果关联的 std::future 对象。
- 执行任务:
- 调用
operator() 或将其传递给线程执行时,任务会被执行,并将返回值或异常自动设置到 std::future 中。
std::packaged_task 的主要方法
| 方法 |
描述 |
operator() |
执行包装的可调用对象,并将返回值或异常与 std::future 关联。 |
get_future() |
获取与 std::packaged_task 关联的 std::future 对象。 |
reset() |
重置 std::packaged_task,使其可以重新包装一个新的可调用对象。 |
具体使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
//函数模板参数指定函数的参数和返回值
//传入的函数不需要严格匹配,但是函数参数应当可以进行隐式转换
//该对象含有operator(),是调用对象,可以传给std::function
//该对象可以移动,但是不能拷贝
int print(int d) {
cout << d << endl;
return d;
}
int main() {
std::packaged_task<int(double)> task(print); //传入函数参数可以发生隐式类型转换即可
std::future<int> fut = task.get_future();
//在main函数执行
task(3.14159265359);
cout << fut.get() << endl; //执行完task才会得到结果
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
int print(int d) {
this_thread::sleep_for(chrono::seconds(5));
cout << d << endl;
return d;
}
void func(packaged_task<int(double)>&& task, const double d) {
task(d);
}
int main() {
packaged_task<int(double)> task(print);
future<int> fut = task.get_future();
//在新线程上面去执行task
thread t1(func,std::move(task),3.1415926);
cout << fut.get() << endl; //等待t1线程的task执行结束才会获得值
t1.join
}
|
std::async和std::packaged_task<>使用对比
std::async
- 将任务(函数)交给它,由它决定立刻执行还是延时执行,并直接返回future对象用来获取返回值。
- std::launch::async立刻执行函数,会创建新线程
- std::launch::deferred不会立刻执行,future对象调用wait或get时候才执行,不会创建新线程
std::packaged_task
- 直接绑定任务,通过get_future成员方法得到future对象,通过oprator()进行任务的调用。
- 手动控制调用的时机
对比下面三个程序
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
int print(int d) {
this_thread::sleep_for(chrono::seconds(5));
cout << d << endl;
return d;
}
void func(future<int>& fut) {
this_thread::sleep_for(chrono::seconds(10));
fut.wait();
}
int main() {
future<int> fut = async(launch::deferred, print, 10);
thread t1(func,std::ref(fut));
cout << fut.get() << endl; //立刻执行get,但是t1线程之后会执行wait导致报错。
t1.join();
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
int print(int d) {
this_thread::sleep_for(chrono::seconds(5));
cout << d << endl;
return d;
}
void func(future<int>& fut) {
this_thread::sleep_for(chrono::seconds(10));
fut.wait();
}
int main() {
future<int> fut = async(launch::deferred, print, 10);
thread t1(func,std::ref(fut));
auto state = fut.wait_for(chrono::seconds(0));
while (state != future_status::ready) { //异步任务还没有结束
state = fut.wait_for(chrono::milliseconds(200)); //继续等待
}
cout << fut.get() << endl; //调用get时候,异步任务以及结束,这里只是获得结果
t1.join();
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
int print(int d) {
this_thread::sleep_for(chrono::seconds(5));
cout << d << endl;
return d;
}
void func(packaged_task<int(int)>&& task, const int d) {
this_thread::sleep_for(chrono::seconds(10));
task(d);
}
int main() {
packaged_task<int(int)> task(print);
future<int> fut = task.get_future();
thread t(func,std::move(task),10);
cout << fut.get() << endl;
t.join();
}
|
std::promise
std::promise 的核心功能
- 用途:
std::promise 用于手动设置一个值或异常,并将其与 std::future 关联。
- 特点:
- 需要显式调用
set_value() 或 set_exception() 来设置值或异常。
- 适用于需要手动控制结果设置的场景。
- 通常用于将结果从一个线程传递到另一个线程。
std::promise 不能复制,但它支持移动语义(Move Semantics)
std::promise 的主要方法
| 方法 |
描述 |
set_value() |
设置值,并将 std::future 标记为就绪。 |
set_exception() |
设置异常,并将 std::future 标记为就绪。 |
get_future() |
获取与 std::promise 关联的 std::future 对象。 |
std::promise 的使用示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
#include <iostream>
#include <thread>
#include <future>
void task(std::promise<int> prom) {
std::this_thread::sleep_for(std::chrono::seconds(2)); // 模拟耗时操作
prom.set_value(42); // 设置值
}
int main() {
std::promise<int> prom;
std::future<int> fut = prom.get_future(); // 获取与 promise 关联的 future
std::thread t(task, std::move(prom)); // 启动线程,传递 promise
std::cout << "Waiting for the result..." << std::endl;
int result = fut.get(); // 阻塞,直到 promise 设置值
std::cout << "Result: " << result << std::endl;
t.join(); // 等待线程结束
return 0;
}
|
future保存异常
- 一下几种情况可以将异常保存到future中,当future.get()的时候会重新抛出异常
- async执行操作的时候发生异常,会将异常保存到future中。
- packaged_task执行任务函数的时候抛出异常,会将异常保存到future中。
- promise对象调用set_exception()设置异常。
- 当async和packaged_task对象在future未就绪的时候被销毁,他们的析构函数就会将std::future_error存储为异步任务的状态,它的值(std::future_error::code()方法获得)是std::future_errc::broken_promise(枚举类型)。