主要介绍一下常用的类或者函数接口

thread

std::thread 线程类,用于启动一个线程,不支持拷贝构造,不支持赋值拷贝,支持移动构造,移动赋值

std::this_thread 命名空间

  • get_id Get thread id (function )
  • yield Yield to other threads (function )
  • sleep_until Sleep until time point (function )
  • sleep_for Sleep for time span (function )

mutex

std::mutex 封装互斥锁的类,可调用lock、unlock进行加锁解锁

std::recursive_mutex 支持一个线程多次加锁(递归场景)

std::timed_mutex 额外提供try_lock_for和try_lock_untile接口,这两个接口跟try_lock类似,只是他不会马上返回,而是直接进入阻塞,直到时间条件到了或者解锁了就会唤醒试图获取锁资源

std::lock_guard C++11提供的⽀持RAII⽅式管理互斥锁资源的类,在构造时加锁,析构时解锁。另外可以在构造时传入std::adopt_lock管理已经加锁的mutex对象

1
2
3
4
5
6
7
std::mutex mtx; 
// ......
{
mtx.lock();
std::lock_guard<std::mutex> lck (mtx, std::adopt_lock)
}
// ......

std::unique_lock lock_guard的进阶版,提供更多的接口;unique_lock在构造的时候传不同的tag,⽤以支持在构造的时候不同的方式处理锁对象

try_to_lock:该对象管理m,并通过调用将其锁定(不阻塞)。锁定失败无返回值,可通过对象本身重载的operator bool判断。m.try_lock()

defer_lock:管理传入的互斥锁对象 ,但不对其进行锁定。且传入的互斥锁当前未被构造线程锁定。

adopt_lock:管理传入的m,并且当前已经被构造线程锁定

**unique_lock **还支持构造时传入 const chrono::duration<Rep,Period>& rel_time或者const chrono::time_point<Clock,Duration>& abs_time支持锁定持续时间或者锁定直至时间点

主要是为了解决什么问题?

std::lock_guard 是极简 RAII 锁:

  • 构造 = 加锁
  • 析构 = 解锁
  • 无法延迟、无法尝试、无法接管、无法手动 unlock

std::unique_lock + 标签,解决了 lock_guard 的三大硬伤:

  1. 无法延迟加锁 → 用 defer_lock
  2. 无法非阻塞尝试加锁 → 用 try_to_lock
  3. 无法接管已手动加的锁 → 用 adopt_lock

同时,这些标签是 std::condition_variable 等待、std::lock 多锁死锁避免 的基础前提。

条件变量要能自动解锁、自动重新加锁 → 必须用 unique_lock + 标签

同时锁多个锁、避免死锁 → 必须用 unique_lock + defer_lock

image-20260412191928407

lock

函数模板,可以⽀持对多个锁对象同时锁定,如果其中一个锁对象没有锁住,lock函数会把已经锁定的对象解锁而进入阻塞,直到锁定所有的所有的对象。

try_lock

也是⼀个函数模板,尝试对多个锁对象进⾏同时尝试锁定,如果全部锁对象都锁定了,返回-1,如果某⼀个锁对象尝试锁定失败,把已经锁定成功的锁对象解锁,返回未锁定对象的索引

**call_once:**多线程执行时,让第一个线程执行Fn一次,其他线程不再执行Fn。

我们可以注意到,几乎所有的锁对象,都不支持拷贝构造,赋值重载,只会支持移动构造或者移动赋值

atomic

具体在另一篇博客中可以找到atomic和无锁设计(CAS)相关内容

condition_variable

std::condition_variable 是 C++11 提供的线程同步机制,通常配合 mutex 和 unique_lock 使用,用于让线程等待某个共享状态发生变化,避免忙轮询。

当线程调用 wait 时,它会原子地释放 mutex 并进入阻塞状态;被 notify_one 或 notify_all 唤醒后,会重新竞争 mutex,拿到锁以后 wait 才真正返回。

因为条件变量存在虚假唤醒,所以通常应该使用带 predicate 的 wait,或者使用 while 循环重新检查条件。

notify_one 用于唤醒一个等待线程,notify_all 则唤醒所有等待线程。

两个线程交替输出奇数偶数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>

std::mutex mtx; // 全局互斥锁:保证同一时间只有一个线程能操作共享资源
std::condition_variable cv; // 全局条件变量:控制线程的等待和唤醒
int num = 1; // 共享计数器:从1开始,逐步递增到20
bool odd_turn = true; // 执行权标记:true=奇数线程执行,false=偶数线程执行

// 输出奇数
void print_odd() {
while (num <= 20) { // 循环直到数字超过20
// 步骤1:加锁,抢占共享资源的访问权
std::unique_lock<std::mutex> lock(mtx);

// 步骤2:等待符合执行条件
cv.wait(lock, [](){ return odd_turn; });
/*
解释:
- `cv.wait()` 会先**释放锁**,让当前线程进入“等待状态”,直到被其他线程唤醒。
- 被唤醒后,会重新**加锁**,并检查后面的条件(lambda表达式):
- 如果 `odd_turn == true`:条件满足,继续执行;
- 如果 `odd_turn == false`:条件不满足,继续等待。
- 这一步是为了避免“虚假唤醒”(线程被唤醒但仍不满足执行条件)。
*/

// 步骤3:输出奇数并更新计数器
if (num <= 20) std::cout << "奇: " << num++ << std::endl;
/*
解释:
- 加了 `if (num <= 20)` 是因为线程唤醒时,num可能已经超过20(比如最后一次循环),避免输出多余数字。
- `num++` 是先输出当前num,再把num加1(比如num=1时,输出1,然后num变成2)。
*/

// 步骤4:切换执行权,唤醒偶数线程
odd_turn = false; // 把开关切到偶数线程
cv.notify_one(); // 唤醒等待在这个条件变量上的一个线程(这里就是偶数线程)
}
}

// 输出偶数
void print_even() {
while (num <= 20) { // 循环直到数字超过20
// 步骤1:加锁
std::unique_lock<std::mutex> lock(mtx);

// 步骤2:等待符合执行条件(和奇数线程相反)
cv.wait(lock, [](){ return !odd_turn; });
/*
解释:
- 条件是 `!odd_turn`(即odd_turn == false),只有偶数线程该执行时才会继续。
- 同样会释放锁等待,被唤醒后重新加锁检查条件。
*/

// 步骤3:输出偶数并更新计数器
if (num <= 20) std::cout << "偶: " << num++ << std::endl;

// 步骤4:切换执行权,唤醒奇数线程
odd_turn = true; // 把开关切回奇数线程
cv.notify_one(); // 唤醒等待的奇数线程
}
}

int main() {
std::thread t1(print_odd), t2(print_even);
t1.join();
t2.join();
return 0;
}

future和async

std::future 是 C++11 提供的异步结果获取机制,它本身不负责执行任务,而是关联一个 shared state,用于获取异步任务最终产生的返回值或者异常。

std::async 是一种高级异步任务接口,可以直接提交一个可调用对象,并返回对应的 future。调用 future::get() 时,如果任务没有完成,当前线程会阻塞,任务完成后返回结果;如果异步任务抛出了异常,异常会保存在 shared state 中,并在 get() 时重新抛出。

std::async 有 std::launch::async 和 std::launch::deferred 两种启动策略。前者要求异步执行,后者直到调用 get 或 wait 时才真正执行。默认策略允许实现自行选择。

future 通常只能调用一次 get(),如果需要多个线程读取同一结果,可以使用 shared_future。


以下由ChatGPT生成,可做参考

1. std::future

std::future<T> 用于接收异步任务未来返回的结果。T 表示结果类型,例如 future<int> 表示未来会得到一个 int,future<string> 表示未来会得到一个 string,future<void> 表示只等待任务完成,不接收具体返回值。

最简单的使用方式是配合 std::async:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <iostream>
#include <future>
#include <thread>
#include <chrono>

using namespace std;

int calculate() {
this_thread::sleep_for(chrono::seconds(2));
return 100;
}

int main() {
// 启动异步任务,返回 future<int>
future<int> f = async(launch::async, calculate);

cout << "主线程继续执行" << endl;

// 获取异步任务结果
// 如果任务还没结束,get() 会阻塞等待
int result = f.get();

cout << "result = " << result << endl;
return 0;
}

常用接口

get()

1
T get();

用于获取异步结果。如果结果还没有准备好,当前线程会阻塞等待。普通 future 的 get() 只能成功调用一次。

1
2
3
4
5
6
7
8
future<int> f = async(launch::async, [] {
return 123;
});

cout << f.get() << endl; // 123

// 再次调用 get() 不合法
// cout << f.get() << endl;

valid()

1
bool valid() const noexcept;

判断当前 future 是否关联有效的共享状态。

1
2
3
4
5
6
7
8
9
future<int> f = async(launch::async, [] {
return 10;
});

cout << boolalpha << f.valid() << endl; // true

int x = f.get();

cout << f.valid() << endl; // false

get() 之后普通 future 会失效。

wait()

1
void wait() const;

等待任务完成,但不取出结果。

1
2
3
4
5
6
7
future<int> f = async(launch::async, [] {
this_thread::sleep_for(chrono::seconds(2));
return 100;
});

f.wait(); // 只等待
cout << f.get() << endl; // 再获取结果

wait_for()

1
2
3
4
template<class Rep, class Period>
future_status wait_for(
const chrono::duration<Rep, Period>& timeout_duration
) const;

等待一段指定时间。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
future<int> f = async(launch::async, [] {
this_thread::sleep_for(chrono::seconds(3));
return 100;
});

future_status status = f.wait_for(chrono::seconds(1));

if (status == future_status::ready) {
cout << "任务完成" << endl;
} else if (status == future_status::timeout) {
cout << "任务还没完成" << endl;
}

cout << f.get() << endl;

wait_until()

等待到某个具体时间点。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
future<int> f = async(launch::async, [] {
this_thread::sleep_for(chrono::seconds(3));
return 100;
});

auto timePoint = chrono::steady_clock::now() + chrono::seconds(1);

future_status status = f.wait_until(timePoint);

if (status == future_status::ready) {
cout << "任务完成" << endl;
} else {
cout << "任务未完成" << endl;
}

future 不能复制,只能移动

1
2
3
4
5
6
future<int> f1 = async(launch::async, [] {
return 100;
});

// future<int> f2 = f1; // 错误,不能复制
future<int> f2 = move(f1); // 正确,可以移动

2. std::shared_future

std::shared_future<T> 与 future<T> 类似,但它可以复制,并且 get() 可以调用多次,适合多个线程共享同一个异步结果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <iostream>
#include <future>

using namespace std;

int main() {
future<int> f = async(launch::async, [] {
return 100;
});

shared_future<int> sf = f.share();

cout << sf.get() << endl;
cout << sf.get() << endl;
cout << sf.get() << endl;

return 0;
}

输出:

1
2
3
100
100
100

future::share()

1
shared_future<T> share();

把 future 转换为 shared_future。

1
2
3
4
5
6
7
8
9
future<int> f = async(launch::async, [] {
return 10;
});

shared_future<int> sf = f.share();

cout << boolalpha;
cout << f.valid() << endl; // false
cout << sf.valid() << endl; // true

调用 share() 后,原来的 future 会失效。

多线程共享 shared_future

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <iostream>
#include <future>
#include <thread>
#include <vector>

using namespace std;

int main() {
future<int> f = async(launch::async, [] {
this_thread::sleep_for(chrono::seconds(2));
return 100;
});

shared_future<int> sf = f.share();

vector<thread> threads;

for (int i = 0; i < 3; ++i) {
threads.emplace_back([sf, i] {
int value = sf.get();
cout << "thread " << i
<< " result = " << value << endl;
});
}

for (auto& t : threads) {
t.join();
}

return 0;
}

future 与 shared_future 对比

特性 future shared_future
get() 通常只能调用一次 可以调用多次
是否可复制 不可以 可以
是否可移动 可以 可以
多线程共享结果 不适合 适合
valid() 支持 支持
wait() 支持 支持
wait_for() 支持 支持
wait_until() 支持 支持

3. std::async

std::async 用于执行一个异步任务,并返回对应的 future。

基本格式:

1
async(执行策略, 函数, 参数...);

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <iostream>
#include <future>

using namespace std;

int add(int a, int b) {
return a + b;
}

int main() {
future<int> f = async(launch::async, add, 10, 20);

cout << f.get() << endl; // 30
return 0;
}

Lambda 同样可以直接传入:

1
2
3
4
5
future<int> f = async(launch::async, [] {
return 123;
});

cout << f.get() << endl;

带参数:

1
2
3
4
5
6
7
8
9
10
future<int> f = async(
launch::async,
[](int a, int b) {
return a * b;
},
10,
20
);

cout << f.get() << endl; // 200

4. std::launch

std::launch 用于指定 async 的执行策略,最常见的是 launch::async 和 launch::deferred。

launch::async

1
2
3
future<int> f = async(launch::async, [] {
return 100;
});

任务以异步方式执行。可以通过线程 ID 观察:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <iostream>
#include <future>
#include <thread>

using namespace std;

int main() {
cout << "main thread: "
<< this_thread::get_id() << endl;

future<void> f = async(launch::async, [] {
cout << "async thread: "
<< this_thread::get_id() << endl;
});

f.get();
return 0;
}

通常主线程和异步任务线程的 ID 不同。

launch::deferred

launch::deferred 表示延迟执行。任务在调用 get() 或 wait() 时才执行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <iostream>
#include <future>
#include <thread>

using namespace std;

int main() {
cout << "main thread: "
<< this_thread::get_id() << endl;

future<int> f = async(launch::deferred, [] {
cout << "task thread: "
<< this_thread::get_id() << endl;

return 100;
});

cout << "async called" << endl;

int result = f.get();

cout << result << endl;
return 0;
}

deferred 任务通常会在调用 get() 的线程中执行。

不指定 launch

也可以直接:

1
2
3
future<int> f = async([] {
return 100;
});

这种情况下标准库可以选择 async 或 deferred。

如果明确要求异步执行,通常直接写:

1
async(launch::async, func);

5. std::future_status

wait_for() 和 wait_until() 会返回 std::future_status。它有三个常见状态:

1
2
3
future_status::ready
future_status::timeout
future_status::deferred

ready

结果已经准备好。

1
2
3
4
5
auto status = f.wait_for(chrono::seconds(1));

if (status == future_status::ready) {
cout << "ready" << endl;
}

timeout

等待时间结束,任务还没有完成。

1
2
3
if (status == future_status::timeout) {
cout << "timeout" << endl;
}

deferred

任务使用了延迟执行策略。

1
2
3
4
5
6
7
8
9
future<int> f = async(launch::deferred, [] {
return 100;
});

future_status status = f.wait_for(chrono::seconds(0));

if (status == future_status::deferred) {
cout << "deferred" << endl;
}

完整示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include <iostream>
#include <future>
#include <thread>
#include <chrono>

using namespace std;

int main() {
future<int> f = async(launch::async, [] {
this_thread::sleep_for(chrono::seconds(3));
return 100;
});

while (true) {
future_status status =
f.wait_for(chrono::milliseconds(500));

if (status == future_status::ready) {
cout << "result = " << f.get() << endl;
break;
}

if (status == future_status::timeout) {
cout << "still running..." << endl;
}

if (status == future_status::deferred) {
cout << "deferred task" << endl;
break;
}
}

return 0;
}

6. std::promise

std::promise<T> 用于主动设置一个结果,另一个线程通过对应的 future<T> 获取结果。

最常用的三个接口:

1
2
3
get_future()
set_value()
set_exception()

基本用法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <iostream>
#include <future>
#include <thread>

using namespace std;

void worker(promise<int> p) {
int result = 10 + 20;

// 设置结果
p.set_value(result);
}

int main() {
promise<int> p;

// 获取对应 future
future<int> f = p.get_future();

// promise 不能复制,所以使用 move
thread t(worker, move(p));

cout << "result = " << f.get() << endl;

t.join();
return 0;
}

输出:

1
result = 30

get_future()

1
future<T> get_future();

获取和 promise 对应的 future。

1
2
promise<int> p;
future<int> f = p.get_future();

一个 promise 一般只能调用一次 get_future()。

1
2
3
4
5
promise<int> p;

auto f1 = p.get_future();

// auto f2 = p.get_future(); // 会抛 future_error

set_value()

设置正常结果。

1
2
3
4
5
6
promise<string> p;
future<string> f = p.get_future();

p.set_value("hello");

cout << f.get() << endl;

对于 promise<void>:

1
2
3
4
5
6
promise<void> p;
future<void> f = p.get_future();

p.set_value();

f.get();

完整示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <iostream>
#include <future>
#include <thread>

using namespace std;

int main() {
promise<void> p;
future<void> f = p.get_future();

thread t([&p] {
cout << "loading..." << endl;

this_thread::sleep_for(chrono::seconds(2));

cout << "finished" << endl;
p.set_value();
});

f.get();

cout << "main thread continue" << endl;

t.join();
return 0;
}

set_exception()

可以把异常保存到共享状态中,future::get() 时重新抛出。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <iostream>
#include <future>
#include <thread>

using namespace std;

void worker(promise<int> p) {
try {
throw runtime_error("calculate failed");
}
catch (...) {
p.set_exception(current_exception());
}
}

int main() {
promise<int> p;
future<int> f = p.get_future();

thread t(worker, move(p));

try {
cout << f.get() << endl;
}
catch (const exception& e) {
cout << e.what() << endl;
}

t.join();
return 0;
}

输出:

1
calculate failed

set_value_at_thread_exit()

在线程退出时再把结果设置为 ready。

1
2
3
4
5
6
7
8
9
10
11
promise<int> p;
future<int> f = p.get_future();

thread t([p = move(p)]() mutable {
p.set_value_at_thread_exit(100);

cout << "thread is still running" << endl;
});

cout << f.get() << endl;
t.join();

对应的异常接口:

1
set_exception_at_thread_exit()

初学阶段了解即可。


7. std::packaged_task

std::packaged_task 用于包装一个可调用对象,并把函数执行结果自动保存到共享状态中。

例如函数:

1
2
3
int add(int a, int b) {
return a + b;
}

可以包装成:

1
packaged_task<int(int, int)> task(add);

其中:

1
int(int, int)

表示函数返回值为 int,参数为两个 int。

基本示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <iostream>
#include <future>

using namespace std;

int add(int a, int b) {
return a + b;
}

int main() {
packaged_task<int(int, int)> task(add);

// 获取 future
future<int> f = task.get_future();

// 执行任务
task(10, 20);

cout << f.get() << endl; // 30
return 0;
}

配合 thread 使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <iostream>
#include <future>
#include <thread>

using namespace std;

int calculate(int x) {
return x * x;
}

int main() {
packaged_task<int(int)> task(calculate);

future<int> f = task.get_future();

// packaged_task 不能复制
thread t(move(task), 10);

cout << "result = " << f.get() << endl;

t.join();
return 0;
}

输出:

1
result = 100

包装 Lambda

1
2
3
4
5
6
7
8
9
10
11
packaged_task<int(int)> task(
[](int x) {
return x * 2;
}
);

future<int> f = task.get_future();

task(50);

cout << f.get() << endl; // 100

常用接口

get_future()

1
future<R> get_future();

获取任务执行结果对应的 future。

1
2
3
4
5
packaged_task<int()> task([] {
return 100;
});

future<int> f = task.get_future();

operator()

调用 packaged_task 就是执行被包装的函数。

1
task(10, 20);

等价于调用其:

1
operator()

返回值不会直接返回给调用者,而是保存到共享状态,由 future 获取。

valid()

判断 packaged_task 当前是否包含有效任务。

1
2
3
4
5
6
7
packaged_task<int()> task([] {
return 100;
});

cout << boolalpha
<< task.valid()
<< endl;

reset()

重新创建共享状态,使同一个任务对象可以再次执行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <iostream>
#include <future>

using namespace std;

int main() {
packaged_task<int(int)> task(
[](int x) {
return x * 2;
}
);

future<int> f1 = task.get_future();

task(10);
cout << f1.get() << endl; // 20

task.reset();

future<int> f2 = task.get_future();

task(20);
cout << f2.get() << endl; // 40

return 0;
}

reset() 不会替换原来的函数,只会重新建立新的共享状态。


8. promise 和 packaged_task 的区别

promise 需要手动设置结果:

1
2
3
4
5
6
promise<int> p;
future<int> f = p.get_future();

int result = calculate();

p.set_value(result);

packaged_task 会自动保存函数返回值:

1
2
3
4
5
packaged_task<int()> task(calculate);

future<int> f = task.get_future();

task();

使用场景可以简单记为:

1
2
3
4
5
6
7
8
promise
适合手动决定什么时候产生结果

packaged_task
适合包装一个函数或 Lambda

async
适合直接启动一个异步任务

9. async、packaged_task、promise 综合对比

async

1
2
future<int> f =
async(launch::async, calculate);

特点:写法最简单,任务执行和结果获取都由标准库处理。

packaged_task

1
2
3
4
5
packaged_task<int()> task(calculate);

future<int> f = task.get_future();

thread t(move(task));

特点:函数和异步结果绑定,但什么时候执行、在哪个线程执行由程序员决定。

promise

1
2
3
4
5
6
7
promise<int> p;
future<int> f = p.get_future();

thread t([p = move(p)]() mutable {
int result = 100;
p.set_value(result);
});

特点:结果什么时候产生完全由程序员控制。


10. 异常传递

future 支持跨线程传递异常。

使用 async 时:

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 <future>

using namespace std;

int main() {
future<int> f = async(
launch::async,
[]() -> int {
throw runtime_error("error");
}
);

try {
int result = f.get();
}
catch (const exception& e) {
cout << e.what() << endl;
}

return 0;
}

异步任务中的异常会保存在共享状态中,调用 get() 时重新抛出。


11. 常见错误

future 重复 get()

1
2
3
4
5
6
7
future<int> f = async(launch::async, [] {
return 100;
});

cout << f.get() << endl;

// cout << f.get() << endl; // 错误

普通 future 的结果只能消费一次。

promise 重复 set_value()

1
2
3
4
5
promise<int> p;

p.set_value(100);

// p.set_value(200); // 抛 future_error

一个共享状态只能设置一次结果。

promise 重复 get_future()

1
2
3
4
5
promise<int> p;

future<int> f1 = p.get_future();

// future<int> f2 = p.get_future(); // 抛 future_error

broken_promise

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
future<int> f;

{
promise<int> p;

f = p.get_future();

// 没有 set_value()
}

// promise 已经被销毁

try {
cout << f.get() << endl;
}
catch (const future_error& e) {
cout << e.what() << endl;
}

如果 promise 被销毁时还没有设置结果,对应的 future 会得到 broken_promise 错误。


12. async 的一个注意点

下面的代码不要随意忽略 async 返回的 future:

1
2
3
async(launch::async, [] {
this_thread::sleep_for(chrono::seconds(2));
});

launch::async 返回的某些 future 在销毁时可能需要等待异步任务完成,所以实际开发中建议保存返回值:

1
2
3
4
5
auto f1 = async(launch::async, task1);
auto f2 = async(launch::async, task2);

f1.get();
f2.get();

13. 综合示例

同时执行三个任务:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include <iostream>
#include <future>
#include <thread>
#include <chrono>

using namespace std;

string query(string name, int seconds) {
cout << name << " start" << endl;

this_thread::sleep_for(
chrono::seconds(seconds)
);

return name + " finished";
}

int main() {
future<string> f1 =
async(launch::async, query, "task A", 3);

future<string> f2 =
async(launch::async, query, "task B", 1);

future<string> f3 =
async(launch::async, query, "task C", 2);

cout << f1.get() << endl;
cout << f2.get() << endl;
cout << f3.get() << endl;

return 0;
}

三个异步任务可以同时运行。


14. 常用接口速查

future

1
get()

获取结果,可能阻塞,只能成功调用一次。

1
valid()

判断是否关联有效共享状态。

1
wait()

等待任务完成。

1
wait_for()

等待指定时间。

1
wait_until()

等待到指定时间点。

1
share()

转换为 shared_future。

shared_future

1
2
3
4
5
get()
valid()
wait()
wait_for()
wait_until()

特点是可以复制,get() 可以多次调用。

promise

1
2
3
4
5
get_future()
set_value()
set_exception()
set_value_at_thread_exit()
set_exception_at_thread_exit()

packaged_task

1
2
3
4
get_future()
operator()()
valid()
reset()

async

1
2
3
async(func, args...)
async(launch::async, func, args...)
async(launch::deferred, func, args...)

future_status

1
2
3
future_status::ready
future_status::timeout
future_status::deferred

15. 最后总结

这一套内容可以直接按下面的方式记:

名称 作用
future 获取一个未来产生的结果
shared_future 多个地方共享同一个未来结果
async 启动异步任务
launch::async 异步执行
launch::deferred 延迟到 get() / wait() 时执行
promise 手动设置异步结果
packaged_task 包装函数并自动保存函数返回值
future_status::ready 结果已经准备好
future_status::timeout 等待超时
future_status::deferred 任务是延迟执行

常见组合:

1
2
3
4
5
6
7
async              -> future

promise -> future

packaged_task -> future

future::share() -> shared_future

学习时重点掌握下面几组代码即可:

1
2
auto f = async(launch::async, func);
auto result = f.get();
1
2
3
promise<int> p;
future<int> f = p.get_future();
p.set_value(100);
1
2
3
packaged_task<int()> task(func);
future<int> f = task.get_future();
task();
1
2
future<int> f = async(launch::async, func);
shared_future<int> sf = f.share();

把这四种写法熟练以后,C++11 这一套 future 相关接口基本就掌握了。