目录

一、引言
本文将介绍C++中常用的异步机制。
二、并发和并行
并发和并行是一对很容易让人分不清的概念,所以在开始谈C++异步前,先来区分一下并发和并行。要理解并发(Concurrency) 与并行(Parallelism) 的区别,核心是抓住 “逻辑上同时” 和 “物理上同时” 的本质差异 —— 前者是 “看起来一起跑”,后者是 “真的一起跑”。
| 概念 | 核心定义 | 依赖条件 |
|---|---|---|
| 并发 | 多个任务在同一时间段内交替执行(同一时刻只有一个任务在跑),营造 “同时进行” 的错觉。 | 单核 CPU 即可实现(依赖任务切换) |
| 并行 | 多个任务在同一时刻同时执行(同一时刻有多个任务在跑),是真正的 “同时进行”。 | 必须依赖多核 / 多 CPU 硬件 |
三、std::future

std::future是一个模板类,在C++官网可以查到,主要就是用来获取异步执行流的结果。
下面列举出future类常用的成员函数:
1)get():等待异步操作完成并返回结果(只能调用一次,多次调用会报错)。
2)wait():等待异步操作完成,但不获取结果。
3)wait_for(duration):等待指定的时间长度。
4)wait_until(time_point): 等待到指定的时间点。
#include <iostream>
#include <future>
#include <thread>
int func() {
std::this_thread::sleep_for(std::chrono::seconds(3));
return 30;
}
int main()
{
// 使用async创建异步任务,后面会详细介绍
std::future<int> fut = std::async(std::launch::async, func);
int result = fut.get();//只能调用一次
std::cout << result << std::endl;
return 0;
}
以上是future如何使用的简单演示。
如果要接受的返回值类型是int,那么就给future的模版类型传int。总之就是需要接收什么类型,就给future传什么类型。上述代码中,std::async会创建一个线程去执行func函数。
四、std::async

std::async是一个函数模板,用于异步运行函数。它返回一个 std::future对象,通过该对象可以获取函数的返回值。
std::async有两种启动策略:
1)std::launch::async:立即在新线程上执行指定任务。
2)std::launch::deferred:延迟执行任务,直到调用wait()或get()时才执行。
#include <iostream>
#include <future>
#include <thread>
int func1(int val) {
std::this_thread::sleep_for(std::chrono::seconds(3));
return val * 2;
}
int func2() {
std::this_thread::sleep_for(std::chrono::seconds(3));
return 30;
}
int main()
{
//立即在新线程上执行
std::future<int> fut1 = std::async(std::launch::async, func1, 10);
//延迟执行
std::future<int> fut2 = std::async(std::launch::deferred, func2);
int result1 = fut1.get();
std::cout << result1 << std::endl;
int result2 = fut2.get();//直到调用get()/wait()才开始执行
std::cout << result2 << std::endl;
return 0;
}
运行上述代码,可以看到的结果是:打印result1后,要等3秒才会打印result2,,原因在于fut2调用get函数时,线程才开始执行func2,而在func2中又让线程休眠了3秒。
五、std::promise

std::promise是一个模板类,用于在一个线程中设置值或异常,然后通过关联的std::future对象在另一个线程中获取这些值或异常。这么说很抽象,看接下来的使用就明白了。
以下是promise常用的一些成员函数:
1)get_future():返回与该 promise 关联的 future 对象。
2)set_value():设置promise的值,使 future 就绪。
3)set_exception():设置异常,使 future 就绪。
#include <iostream>
#include <future>
#include <thread>
void func(std::promise<int>& pro) {
std::this_thread::sleep_for(std::chrono::seconds(3));
pro.set_value(30);//设置值
}
int main()
{
std::promise<int> pro;//内置future
std::future<int> fut = pro.get_future();//获取promise内的future
std::thread pro_thread(func, std::ref(pro));
int result = fut.get();//获取值
std::cout << result << std::endl;
pro_thread.join();
return 0;
}
新线程执行func函数,设置值,在主线程中,因为获取了pro内部的future,所以可以在主线程通过这个future获取设置的值。
六、std::shared_future


std::shared_future与std::future类似,但它可以被多次复制和多次调用get()方法。这在需要多个线程获取同一个异步操作结果时非常有用。
常用的成员函数与std::future类似。
#include <iostream>
#include <future>
#include <thread>
#include <vector>
int func() {
std::this_thread::sleep_for(std::chrono::seconds(3));
return 30;
}
void print(std::shared_future<int> shared_fut, int id) {
std::this_thread::sleep_for(std::chrono::seconds(3));
int result = shared_fut.get();
std::cout << "[" << id << "]" << result << std::endl;
}
int main()
{
std::future<int> fut = std::async(std::launch::async, func);
std::shared_future<int> shared_fut = fut.share();//转换为shared_future
//创建三个线程去调用get获取异步执行func函数的返回值
std::vector<std::thread> threads;
for (int i = 0; i < 3; i++) {
threads.emplace_back(print, shared_fut, i);
}
for (auto& th : threads) {
th.join();
}
return 0;
}
七、std::packaged_task

std::packaged_task是一个模板类,它包装一个可调用对象(函数、lambda 表达式等),并允许异步获取其结果。它将任务的执行结果(返回值)保存到它内置的future内,然后在外部通过获取它的内置future可以获取被包装任务的执行结果。
常用成员函数:
1)重载的operator()。
2)get_future():返回内置的future。
#include <iostream>
#include <future>
#include <thread>
int main()
{
//包装一个lambda
std::packaged_task task([](int x) {
return x * x;
});
//获取packaged_task内置的future
std::future<int> fut = task.get_future();
//把任务交一个线程去执行
std::thread task_thread(std::ref(task), 5);
//通过内置的future获取任务执行的结果
int result = fut.get();
std::cout << result << std::endl;
task_thread.join();
return 0;
}
八、结语
要更好地理解这些内容,需要将其应用到实际问题的解决场景中。
完~
2223

被折叠的 条评论
为什么被折叠?



