C++ 中的异步编程:从 std::async 到协程
C++ 中的异步编程是创建高性能和响应式应用程序的强大工具。在现代世界中,多核处理器已成为标准,高效管理并行任务的能力是每位 C++ 开发者的必备技能。本文将讲解关键概念,从标准库的基本机制开始,一直到 C++20 的现代协程。
为什么需要异步编程?
异步编程的主要目标是不阻塞程序的主执行线程。想象一下,你的应用程序需要从网络下载一个文件,对其进行处理,然后展示给用户。如果你同步执行这些操作,界面会在下载期间"卡住"。异步方法允许你在"后台"运行一个耗时操作,同时主线程继续处理用户输入。
C++ 中异步的基本方法
在 C++ 中,有几种实现异步执行的方式:
- 手动线程管理(std::thread) — 一种低级但灵活的方法。
- std::async 和 std::future — 用于启动异步任务的高级机制。
- std::packaged_task 和 std::promise — 用于更精细的控制。
- 协程(C++20) — 编写异步代码的现代而优雅的方式。
使用 std::async 和 std::future
这是启动异步任务最简单的方式。std::async 函数返回一个 std::future 对象,该对象将包含执行结果。
#include <iostream>#include <future>#include <thread>#include <chrono>
int heavy_computation(int x) { std::this_thread::sleep_for(std::chrono::seconds(2)); return x * x;}
int main() { std::cout << "正在启动异步任务..."; std::future<int> result = std::async(std::launch::async, heavy_computation, 42); // 在任务运行期间做其他事情 std::cout << "主线程未被阻塞!"; // 获取结果(如果任务尚未完成,则阻塞) int value = result.get(); std::cout << "结果: " << value << ""; return 0;}std::launch::async 标志保证任务将在单独的线程中执行。如果未指定,实现可能会将执行延迟到调用 .get() 时。
使用 std::promise 和 std::packaged_task
这些机制提供了更多的控制。std::promise 允许你"承诺"一个稍后设置的值,而 std::packaged_task 将一个可调用对象包装成一个任务。
#include <iostream>#include <future>#include <thread>
void set_value(std::promise<int>&& prom) { std::this_thread::sleep_for(std::chrono::seconds(1)); prom.set_value(100);}
int main() { std::promise<int> prom; std::future<int> fut = prom.get_future(); std::thread t(set_value, std::move(prom)); std::cout << "正在等待结果..."; int value = fut.get(); std::cout << "收到: " << value << ""; t.join(); return 0;}重要提示: std::promise 和 std::future 是仅可移动的对象。它们不能被复制,只能被移动。
C++20 中的协程:异步的新层次
协程是可以暂停执行并在稍后恢复的函数。它们允许你像编写同步代码一样编写异步代码。