Wiki
future, async, promise and packaged_task
Existing English translations were reused. 1 current source blocks remain in Simplified Chinese, without triggering paid translation.
std::thread Very suitable for expressing:
启动一个线程
↓
在线程里执行某个函数
But it itself does not directly solve:
线程函数怎么把返回值交回来?
线程里抛出的异常怎么传回来?
主线程怎么知道结果准备好了?
多个线程之间怎么建立“结果通道”?
C++11 in <future> provides a set of concurrency utilities that are more oriented toward "tasks and results":
std::future
std::async
std::promise
std::packaged_task
std::shared_future
Their core idea can be summarized in one sentence:
The result is not available yet, but it will be produced in the future; first obtain a future, and then wait for or retrieve the result later.
Therefore:
std::thread
→ 更关心“线程本身”
std::future / std::async / std::promise
→ 更关心“任务最终产生什么结果”
1.Future and async basics
1.1.std::future<T>What is it
std::future<T> It can be understood as:
A read end for a “future result”.
For example:
std::future<int>
Indicates:
未来某个时刻
会有一个 int 结果
If the task has no return value, you can use:
std::future<void>
1.2.Behind future, there is actually a shared state
future is itself neither a result nor a thread.
It is associated with one:
Shared state
It can be understood as:
生产者
写入结果 / 异常
↓
shared state
↓
future
读取结果 / 异常
Shared state may store:
任务返回值
任务抛出的异常
结果是否已经准备好
等待和同步所需的信息
Therefore:
future.get();
What is actually done can be understood as:
如果结果还没准备好
→ 等待
结果准备好以后
→ 取出结果
如果任务保存的是异常
→ 在这里重新抛出
1.3.The simplest std::async
Let's look at a simplest example:
#include <future>
#include <print>
int calculate(int a, int b)
{
return a + b;
}
int main()
{
std::future<int> result = std::async(
std::launch::async,
calculate,
10,
20);
std::println("main continues");
std::println("{}", result.get());
}
Possible output:
main continues
30
Here:
std::async(
std::launch::async,
calculate,
10,
20);
It can be understood as:
提交 calculate(10, 20) 这个任务
↓
让它异步执行
↓
返回一个 future<int>
This:
std::future<int> result
It is the reading end of future results.
Main thread continues execution:
std::println("main continues");
Until:
result.get();
If the task has already been completed:
直接取得结果
If the task is not yet complete:
当前线程在这里等待
Therefore:
std::asyncResponsible for "submitting tasks",std::futureresponsible for "retrieving results later".
1.4.asyncThe parameters of thread are very similar to those of
Basic form:
std::async(policy, callable, arg1, arg2, ...);
For example:
auto result = std::async(
std::launch::async,
calculate,
10,
20);
Among them:
policy
→ 任务怎么启动
callable
→ 要执行的函数 / lambda / 函数对象
arg1, arg2...
→ 传给这个可调用对象的参数
A callable object can be:
普通函数
lambda
函数对象
成员函数
If passing by reference is required, the same considerations apply:
std::ref(x)
as well as the lifetime issue of referenced objects.
2.async startup strategy
2.1.std::launch::async
std::asyncOne of the most commonly used startup strategies is:
std::launch::async
For example:
auto result = std::async(
std::launch::async,
task);
It means:
Require this task to be executed asynchronously.
It can first be intuitively understood as:
现在把任务安排出去执行
当前线程继续干自己的事
以后通过 future 等结果
Key point:
调用方关心的是任务和结果
而不是手动管理 thread 对象
So it and:
std::thread
The perspectives on usage are different.
std::thread More like:
我要创建并管理一个线程
std::async More like:
我要提交一个任务
最后拿它的结果
2.2.std::launch::deferred
Another startup strategy is:
std::launch::deferred
For example:
auto result = std::async(
std::launch::deferred,
task);
It does not immediately put the task into background execution.
It means:
First note this task down, and execute it later when someone is actually waiting for the result.
For example:
#include <future>
#include <print>
#include <thread>
int task()
{
std::println(
"task thread: {}",
std::this_thread::get_id());
return 42;
}
int main()
{
std::println(
"main thread: {}",
std::this_thread::get_id());
auto result = std::async(
std::launch::deferred,
task);
std::println("before get");
std::println("{}", result.get());
}
It can be understood as:
创建 async
↓
task 此时还没执行
↓
main 继续运行
↓
执行 result.get()
↓
就在调用 get() 的线程中执行 task()
↓
得到结果
So deferred is not:
后台线程晚一点执行
But closer to:
延迟到真正需要结果时
再由等待结果的线程执行
2.3.async and deferred difference
It can be simply remembered as:
std::launch::async
→ 任务异步执行
→ 当前线程可以继续
→ future 以后取结果
And:
std::launch::deferred
→ 任务先不执行
→ 第一次 get()/wait() 时才执行
→ 由调用等待函数的线程执行
Therefore:
async
≈ “现在就安排执行”
deferred
≈ “先记下来,用到结果时再执行”
2.4.What happens if no launch policy is written?
You can write it like this:
auto result = std::async(task);
However, in this case, the standard allows implementations to choose for themselves:
async
或者
deferred
So do not put:
std::async(task);
Automatically understood as:
一定立刻创建后台线程并发执行
If you explicitly require:
I just want the task to truly execute asynchronously.
Then it is best to write it explicitly:
std::launch::async
For example:
auto result = std::async(
std::launch::async,
task);
3.future waiting and exceptions
3.1.future::get()
get() is future one of the most important functions:
auto value = future.get();
It does two things:
1. 等待结果准备好
2. 取得结果
For example:
auto result = std::async(
std::launch::async,
[] {
return 42;
});
int value = result.get();
If the task has not yet finished executing:
get()
→ 等
After the task is completed:
get()
→ 返回 42
3.2.Normal future::get() can usually only be called once
For example:
auto result = std::async(
std::launch::async,
[] {
return 42;
});
int value = result.get();
Here:
result.get();
took the result out of this future.
Afterwards:
result.valid()
Usually becomes:
false
Therefore, ordinary std::future can be understood as:
A one-time pickup credential.
结果准备好
↓
get()
↓
结果被取走
↓
future 不再关联有效共享状态
Therefore, do not write it like this:
result.get();
result.get();
Second time get() will perform an invalid future operation, which usually throws:
std::future_error
3.3.future::valid()
You can use:
future.valid()
Query:
Whether this future is still associated with valid shared state.
For example:
auto result = std::async(
std::launch::async,
[] {
return 42;
});
std::println("{}", result.valid());
result.get();
std::println("{}", result.valid());
It can be understood as:
get() 前
→ future 还关联共享状态
get() 后
→ 普通 future 的结果已经被消费
Therefore:
valid()
No:
“任务成功了吗?”
Instead:
“这个 future 现在还有没有关联一个有效共享状态?”
3.4.future::wait()
If you only want to:
Wait for the task to finish, but do not retrieve the result for now.
Possible:
future.wait();
For example:
result.wait();
std::println("task finished");
int value = result.get();
Here:
wait()
Only responsible for waiting.
It will not take the result away.
So it is still possible to:
result.get();
It can be remembered as:
wait()
→ 等待,但不消费结果
get()
→ 等待 + 取走结果
3.5.wait() will not directly throw task exceptions
Assume that the asynchronous task throws an exception.
Call:
future.wait();
Just wait for the shared state to become ready.
It will not rethrow the task exception here.
Exceptions usually occur when:
future.get();
rethrow.
Therefore:
wait()
→ 确认任务已经结束
get()
→ 真正取得“结果或者异常”
3.6.future::wait_for()
You can wait for a period of time:
auto status = result.wait_for(
std::chrono::milliseconds(100));
The return value may be:
std::future_status::ready
std::future_status::timeout
std::future_status::deferred
Respectively represent:
ready
→ 结果已经准备好了
timeout
→ 等待时间到了,但结果还没准备好
deferred
→ 这个任务使用 deferred 策略
Example:
#include <chrono>
#include <future>
#include <print>
#include <thread>
using namespace std::chrono_literals;
int main()
{
auto result = std::async(
std::launch::async,
[] {
std::this_thread::sleep_for(1s);
return 42;
});
if (result.wait_for(100ms)
== std::future_status::timeout)
{
std::println("not ready yet");
}
std::println("{}", result.get());
}
Output:
not ready yet
42
The whole process is:
任务开始
↓
main 最多等 100ms
↓
任务还没完成
↓
wait_for() 返回 timeout
↓
main 继续
↓
最后 get() 等待并取得结果
3.7.future_status::deferred
If:
wait_for(...)
Return:
std::future_status::deferred
Description:
This task is not executing slowly in the background at all.
It is just a deferred task.
For example:
auto result = std::async(
std::launch::deferred,
task);
At this point, keep writing:
result.wait_for(100ms);
result.wait_for(100ms);
result.wait_for(100ms);
it will not "just wait and complete in the background."
Because the logic of deferred is:
真正执行 get()/wait()
↓
调用这些函数的线程亲自执行任务
3.8.Exceptions in asynchronous tasks are propagated back through the future
This is future a very important ability.
For example:
#include <future>
#include <print>
#include <stdexcept>
int main()
{
auto result = std::async(
std::launch::async,
[]() -> int {
throw std::runtime_error("task failed");
});
try
{
std::println("{}", result.get());
}
catch (const std::exception& e)
{
std::println("{}", e.what());
}
}
Output:
task failed
Here, what the task thread throws:
std::runtime_error
It does not directly "jump" from a worker thread to the main thread.
The actual logic is:
异步任务抛异常
↓
异常被保存到 shared state
↓
main 调用 future.get()
↓
get() 发现共享状态里保存的是异常
↓
在 main 这里重新抛出
Therefore:
future.get();
What is obtained is not necessarily:
正常返回值
It could also be:
任务失败时保存的异常
This makes error handling for asynchronous tasks very similar to that of ordinary functions:
普通函数
→ 调用处 catch
异步任务
→ future.get() 处 catch
4.std::promise<T>
Previous:
std::async
A result channel will be created automatically.
But sometimes we want to control it ourselves:
什么时候写入结果
在哪个线程写入结果
写入正常值还是异常
In this case, you can use:
std::promise<T>
It can be understood as:
The "write end" of the result channel corresponding to future.
The relationship is:
promise<T>
│
│ set_value()
│ set_exception()
↓
shared state
↓
future<T>
│
│ get()
↓
读取结果
So the simplest way to remember it is:
promise
→ 写
future
→ 读
4.1.promise::get_future()
Create:
std::promise<int> promise;
In the future, you can use:
std::future<int> future = promise.get_future();
Obtain the read end associated with this promise.
Therefore:
promise
和
future
connected through the same shared state.
4.2.The simplest example of promise
#include <future>
#include <print>
#include <thread>
void producer(std::promise<int> promise)
{
promise.set_value(42);
}
int main()
{
std::promise<int> promise;
std::future<int> future =
promise.get_future();
std::thread thread(
producer,
std::move(promise));
std::println("{}", future.get());
thread.join();
}
Output:
42
The entire process can be understood as:
main 创建 promise
↓
main 从 promise 拿到 future
↓
main 把 promise 移动给 worker
↓
worker 调用 set_value(42)
↓
42 被写入 shared state
↓
main 的 future.get()
↓
读出 42
4.3.Why should Promise be std::move
std::promise It cannot be copied at will.
It is:
move-only
Therefore:
std::thread thread(
producer,
std::move(promise));
is moving ownership of the promise to the worker thread.
It can be understood as:
main
→ 保留 future,负责读
worker
→ 拿走 promise,负责写
Like so:
worker 写结果
↓
shared state
↓
main 读结果
it was established.
4.4.promise::set_value()
The production side can:
promise.set_value(42);
It means:
Write the final result to the shared state.
After writing:
future
→ ready
Waiting for:
future.get();
Execution can continue.
For:
std::promise<void>
No specific return value.
Possible:
promise.set_value();
Indicates:
The task has been successfully completed.
4.5.The result of a promise can usually only be set once.
For example:
promise.set_value(42);
promise.set_value(100);
is incorrect.
It can only be completed once for the same shared state.
First time:
set_value(42);
The result has already been determined.
Second time again:
set_value(...)
Typically throws:
std::future_error
So it can be understood as:
一个 promise
→ 最终只能提交一次最终结果
4.6.promise::set_exception()
promise can not only write normal values:
promise.set_value(result);
Exceptions can also be written:
promise.set_exception(
std::current_exception());
Common notation:
void producer(std::promise<int> promise)
{
try
{
int result = do_work();
promise.set_value(result);
}
catch (...)
{
promise.set_exception(
std::current_exception());
}
}
Such consumer:
future.get();
Yes:
任务成功
→ 返回结果
任务失败
→ 重新抛出保存的异常
4.7.Complete promise exception example
#include <exception>
#include <future>
#include <print>
#include <stdexcept>
#include <thread>
void producer(std::promise<int> promise)
{
try
{
throw std::runtime_error("failed");
}
catch (...)
{
promise.set_exception(
std::current_exception());
}
}
int main()
{
std::promise<int> promise;
auto future = promise.get_future();
std::thread thread(
producer,
std::move(promise));
try
{
std::println("{}", future.get());
}
catch (const std::exception& e)
{
std::println("{}", e.what());
}
thread.join();
}
Output:
failed
The process is:
worker 发生异常
↓
catch 捕获异常
↓
promise.set_exception(...)
↓
异常进入 shared state
↓
main 调用 future.get()
↓
异常在 main 中重新抛出
4.8.What is a broken promise
Assumption:
std::future<int> future;
{
std::promise<int> promise;
future = promise.get_future();
}
Here, when the promise leaves the scope:
没有 set_value()
也没有 set_exception()
But the future is still awaiting the outcome.
This situation is called:
broken promise
That is:
原本负责生产结果的人已经消失
但结果还没有产生
The standard library will not let a future wait forever in vain.
Afterwards:
future.get();
You will get a corresponding:
std::future_error
It can be understood as:
"As a result, the production end has already been cut off; there will be no more normal results."
4.9.All termination paths of a promise should have a definite result.
If using:
std::promise
The production side should handle this carefully:
成功
→ set_value()
失败
→ set_exception()
提前退出 / 取消
→ 也要设计清楚最终结果
Otherwise, it is easy to encounter:
broken promise
5.std::packaged_task
std::packaged_task solves another class of problems.
Assume the following already exists:
int add(int a, int b)
{
return a + b;
}
Now I want to turn it into:
一个“执行以后自动把结果写到 future”的任务
Can be used:
std::packaged_task
It can be understood as:
Wrap a callable object into a task object that "carries a future result channel."
5.1.packaged_task Simplest example
#include <future>
#include <print>
int add(int a, int b)
{
return a + b;
}
int main()
{
std::packaged_task<int(int, int)> task(add);
std::future<int> result =
task.get_future();
task(10, 20);
std::println("{}", result.get());
}
Output:
30
Here:
std::packaged_task<int(int, int)>
Indicates:
这个任务:
接收两个 int
返回一个 int
Create:
std::packaged_task<int(int, int)> task(add);
Later:
task(10, 20);
Actually, it will:
调用 add(10, 20)
↓
得到返回值 30
↓
自动把 30 写入 shared state
↓
result.get() 取得 30
5.2.packaged_task and the biggest difference from promise
promise is:
You decide when to write the result.
For example:
promise.set_value(42);
And packaged_task is:
You give it a function, and when this function is executed, it automatically writes the function's return value to the result channel.
Therefore:
promise
→ 手动写结果
packaged_task
→ 执行函数,自动写结果
5.3.packaged_task automatically propagates the exception
Assume the wrapped function:
int work()
{
throw std::runtime_error("failed");
}
Packaged as:
std::packaged_task<int()> task(work);
Execute:
task();
if work() throws an exception, packaged_task will save the exception to the shared state.
Afterwards:
future.get();
will rethrow this exception.
So it not only handles automatically:
返回值
It will also automatically handle:
异常
5.4.packaged_task in conjunction with thread
You can hand a packaged task to a thread for execution:
#include <future>
#include <print>
#include <thread>
int calculate()
{
return 42;
}
int main()
{
std::packaged_task<int()> task(calculate);
std::future<int> result =
task.get_future();
std::thread thread(
std::move(task));
std::println("{}", result.get());
thread.join();
}
Output:
42
Here:
std::thread thread(
std::move(task));
Indicates:
把 task 移动给新线程
↓
新线程执行 task()
↓
task 内部调用 calculate()
↓
返回值写入 shared state
↓
result.get() 得到 42
5.5.packaged_task also move-only
Similar to promise:
std::packaged_task
也不能随意复制。
Typically required:
std::move(task)
Move it to:
线程
任务队列
worker
in.
5.6.Why packaged_task is well suited for task queues
Thread pools typically embody this idea:
用户提交函数
↓
包装成任务对象
↓
放进任务队列
↓
worker 线程取任务
↓
执行任务
↓
结果写进 shared state
↓
用户拿 future 等结果
This is exactly consistent with:
std::packaged_task
design fits very well.
Because the packaged task itself already contains:
怎么执行任务
+
结果写到哪里
are bound together.
5.7.promise and packaged_task How to choose
You can remember it like this:
5.7.1.promise
Suitable for:
我自己决定结果什么时候产生
结果不一定直接来自某个函数返回值
生产过程可能分很多步骤
For example:
promise.set_value(result);
Controlled by yourself.
5.7.2.packaged_task
Suitable for:
我已经有一个函数 / lambda
我就想把它包装成一个“执行后有 future 结果”的任务
For example:
std::packaged_task<int()> task(calculate);
Then:
task();
Automatically sends the return value into the future.
So the simplest distinction is:
promise
→ 手动生产结果
packaged_task
→ 包装可调用对象,自动生产结果
6.std::shared_future
Normal:
std::future<T>
More suitable:
一个消费者
Because:
future.get();
Usually it can only be consumed once.
If:
Multiple threads need to read the same asynchronous result
Can be used:
std::shared_future<T>
6.1.Convert future to shared_future
For example:
auto future = std::async(
std::launch::async,
[] {
return 42;
});
std::shared_future<int> shared =
future.share();
Afterward, you can:
shared.get();
shared.get();
shared.get();
Multiple places can all read the same result.
6.2.shared_future more like "shared viewing"
Ordinary future:
get()
→ 把结果取走
→ future 失效
And shared future:
get()
→ 查看共享结果
→ 不会因为这次 get() 就把结果消费掉
Therefore:
future
→ 单消费者
shared_future
→ 多消费者
For example:
一次性加载配置
↓
得到 shared_future<Config>
↓
多个线程等待同一份配置
↓
全部读取同一个结果
7.Relationships and Choices
7.1.What exactly is the relationship between these few things?
The entire <future> system can be understood as:
shared state
│
┌─────────────┴─────────────┐
│ │
写入端 读取端
│ │
│ future
│ shared_future
│
├─ promise
│ → 手动写结果
│
├─ packaged_task
│ → 执行函数后自动写结果
│
└─ async
→ 提交任务并自动管理结果通道
Among them:
future
→ 负责以后读取结果
promise
→ 负责手动写结果
packaged_task
→ 把函数包装成“执行后自动写结果”的任务
async
→ 最方便的高级接口,直接提交任务并得到 future
shared_future
→ 允许多个消费者读取同一个结果
7.2.async and thread How to choose
If you are concerned about:
我需要一个长期存在的线程
线程里面一直循环
我要控制它什么时候停止
我要管理生命周期
More suitable:
std::thread
std::jthread
For example:
串口接收线程
传感器采集线程
机器人后台 worker
长期循环任务
If you are concerned about:
我有一个任务
它会算出一个结果
我以后要取得这个结果
任务中的异常也要传回来
More suitable:
std::async
std::future
For example:
并行计算一段结果
后台加载文件
异步计算某个值
7.3.async Not a thread pool
Do not treat:
std::async
Understand it as:
C++ 标准线程池
It is not a standard thread pool interface.
If you need:
固定数量 worker
不断提交大量小任务
任务队列
长期后台执行
可控停止
Usually, the following should be considered:
线程池
std::jthread
专门的任务系统
更完整的异步框架
Instead of constantly:
std::async(...)
8.Common Errors
8.1.Error: assumes that std::async() definitely creates a new thread
For example:
std::async(task);
If no policy is specified, the standard allows the choice of:
async
或
deferred
If true asynchrony is explicitly required:
std::async(
std::launch::async,
task);
8.2.Error: repeated for ordinary futureget()
Error:
future.get();
future.get();
General future results can usually only be consumed once.
8.3.Error: forgetting get() will rethrow the exception
For example:
auto result = std::async(
std::launch::async,
task);
result.get();
Here, not only may a return value be obtained, but an exception from the task may also be thrown.
Therefore, when necessary:
try
{
result.get();
}
catch (...)
{
}
8.4.Error: promise was destroyed without writing a result
If promise:
既没有 set_value()
也没有 set_exception()
it disappears, and the future will get:
broken promise
8.5.Error: Treating promise / packaged_task as a copyable object
They are usually:
move-only
When transferring across threads, it is often necessary to:
std::move(...)
8.6.Error: use async for long-term background loops
For example:
std::async(
std::launch::async,
[] {
while (true)
{
...
}
});
If this task requires:
明确停止
长期运行
生命周期管理
Usually:
std::jthread
more semantically consistent.
9.Final summary
The most essential difference among this set of tools can be remembered as:
std::future
→ “以后我要读结果”
std::promise
→ “我手动把结果写进去”
std::packaged_task
→ “我执行这个函数,结果自动写进去”
std::async
→ “帮我提交任务,并直接给我 future”
std::shared_future
→ “多个地方都要读同一个结果”
Among them:
std::thread / std::jthread
→ 更偏线程生命周期
std::future / std::async
→ 更偏任务与结果
If you only remember one figure:
生产结果
↓
promise / packaged_task / async
↓
shared state
↓
future / shared_future
↓
读取结果
This is the core logic of the entire <future> system.