当前位置: 首页 > article >正文

多线程编程入门-std::future

异步线程执行结果获取

  • std::future
    • std::future::get
    • std::future::wait
  • std::promise
  • std::async
  • std::packaged_task

std::future

std::future是一个类模板,经常被用于获取异步线程或者RPC远程调用的执行结果
就像这样:

std::future<int> m_future= AsyncCall(.....);  
//AsyncCall是一个远程RPC调用函数
result = m_future.get(); 
//我们通过m_future的get函数的返回值即使调用结果

std::future::get

等待异步调用结束,并获取异步调用的返回值,在执行期间,线程会被阻塞住,直到异步调用的结果已经被std::promise设置好了,或者结束了之后才会解除阻塞状态。

必须强调的是,在调用这个函数前,std::future必须已经跟 std::promise, std::async, std::packaged_task等进行绑定,否则调用get函数会出现异常

std::future::wait

等待异步调用结束,但是并不获取异步调用的返回值,其余均与std::future::get一致

这里看不懂没关系,下面有简单的实际使用案例:

std::promise

使用示例:

#include <iostream>
#include <future>
#include <thread>
#include <time.h>
#include <chrono>

int main()
{
    std::promise<int> prom;
    std::future<int> m_future = prom.get_future();
    
    std::thread* m_thread = new std::thread([&prom](){
        
        //阻塞5秒
        std::this_thread::sleep_for(std::chrono::milliseconds(5000));
        prom.set_value(666);
    });
    
    auto start = std::chrono::high_resolution_clock::now();
    std::cout << "future:" << m_future.get() << std::endl;
    auto end = std::chrono::high_resolution_clock::now();

    auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
    // 输出结果
    std::cout << "time:" << duration.count() << " milliseconds" << std::endl;

    return 0;
}

在std::future执行get函数之前,我们首先要使用std::promise的get_future函数与std::future进行一个绑定,换一种浪漫的说法就是相当于给对方一个承诺,让它暂时跳过获取异步线程的执行结果,等到异步线程执行过程中,再找一个合适的机会通过std::promise::set_value将这个返回值设置进来,并能让std::future通过get函数获取到。

并且根据这段代码我们可以清楚的看出,std::future会将当前线程阻塞住,直到std::promise::set_value被调用之后才接触阻塞。

std::async

std::async是一个函数模板,它可以用于创建一个异步任务

函数签名:

std::future<Ret> std::async(std::launch policy, Callable&& f, Args&&... args);

其中第一个参数是aync的使用策略,分为以下几种

1.std::launch::async:强制异步任务在新线程中运行。
2.std::launch::deferred:异步任务不会立即执行,而是延迟到调用 std::future::get 或 std::future::wait 时才执行。但是并不是在新线程中执行
3.std::launch::async | std::launch::deferred:默认值,由运行时根据情况选择执行方式。

#include <iostream>
#include <future>
#include <chrono>

int compute() {
    std::this_thread::sleep_for(std::chrono::seconds(2));
    return 42;
}

int main() {
    std::future<int> fut = std::async(std::launch::async, compute);
    std::cout << "Waiting for result...\n";
    int result = fut.get();
    std::cout << "Result: " << result << '\n';
    return 0;
}

当然,async跟其他函数一样,不仅能根据函数指针绑定函数,也能直接绑定类成员函数,lamda表达式,std::function可调用对象

绑定类成员函数

std::future<int> fut = std::async(std::launch::async, &Worker::doWork, &worker, 10);

绑定lamda表达式

std::future<int> fut = std::async(std::launch::async, [&worker]() {
    return worker.doWork(10);
});

绑定function

std::function<int(int,int)> func = std::bind(&Worker::doWork, &worker, std::placeholders::_1);
std::future<int> fut =  std::async(std::launch::async, func,10);

std::packaged_task

std::packaged_task是一个类模板,用于将一个可调用对象包装成一个异步任务,后续在新线程中执行这个异步任务

#include <iostream>
#include <future>
#include <thread>

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();
    std::thread t(std::move(task), 3, 4);
    std::cout << "Result: " << result.get() << std::endl;
    t.join();
    return 0;
}

首先需要通过一个可调用对象进行构造

构造之后需要在std::future::get调用之前使用 std::packaged_task::get_future与std::future对象进行绑定

在绑定之后,就可以将这个异步任务放到新线程中执行,这里需要重点说明的是

 std::thread t(std::move(task), 3, 4);

std::packaged_task对象比较特殊,其不接受拷贝,只接受移动构造,所以再将它放入新线程运行时,需要先用std::move将其转化为右值用于移动构造。

std::packaged_task与std::async的不同是,std::async是一个函数,他在被调用时,异步任务就已经开始执行。而std::packaged_task是一个类模板用于包装异步任务,异步任务在被包装好之后,使用者可以灵活选择异步任务被执行的时机。

另外std::packaged_task也可以直接用函数指针,lamda表达式,std::function来构造,但是无法用类的成员函数构造,如果想用类的成员函数构造,需要先用std::bind将其包装成function,再用function来进行构造

lamda表达式

std::packaged_task<int(int)> task([worker](int value) {
        return worker.doWork(value);
    });

std::function

std::function<int(int)> func = std::bind(&Worker::doWork, &worker, std::placeholders::_1);
std::packaged_task<int(int)> task(func);

http://www.kler.cn/a/539354.html

相关文章:

  • 了解“/linux-5.4.31/drivers/of/device.c”中的of_device_get_match_data()
  • Linux中设置开机运行指令
  • 在请求时打印出实际代理的目标地址
  • mysql8.0使用MHA实现高可用
  • CSS 相关知识
  • 【C++篇】 异常处理
  • 第十二天 学习ArkUI的交互事件和动画效果
  • HarmonyOS 5.0应用开发——NodeContainer自定义占位节点
  • 每日一题——数组中出现次数超过一半的数字
  • 生成式聊天机器人 -- 基于Pytorch + Global Attention + 双向 GRU 实现的SeqToSeq模型 -- 下
  • AI安全最佳实践:AI云原生开发安全评估矩阵(下)
  • ESP32S3基于espidf ADC使用
  • Leetcode Hot100 76-80
  • 【算法-动态规划】、子序列累加和必须被7整除的最大累加和
  • 机器学习 网络安全 GitHub 机器人网络安全
  • 工业 4G 路由器助力消防领域,守卫生命安全防线
  • ASP.NET Core SignalR的分布式部署
  • 【Uniapp-Vue3】UniCloud云数据库获取指定字段的数据
  • 【蓝桥杯嵌入式】8_IIC通信-eeprom读写
  • 【Android开发AI实战】选择目标跟踪基于opencv实现——运动跟踪
  • 硬盘会莫名增加大量视频和游戏的原因
  • MoMask:可将文本描述作为输入并生成相应的高质量人体运动动作
  • 三种Excel文本连接方法!
  • C#Halcon窗体鼠标交互生成菜单
  • Android网络优化之-HTTPDNS
  • PHP-trim