C++异步编程

目录

一、引言

二、并发和并行

三、std::future

四、std::async

五、std::promise

六、std::shared_future

七、std::packaged_task

八、结语



一、引言

本文将介绍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;
}

八、结语

要更好地理解这些内容,需要将其应用到实际问题的解决场景中。


完~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值