惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

酷 壳 – CoolShell
酷 壳 – CoolShell
H
Hacker News: Front Page
P
Palo Alto Networks Blog
T
ThreatConnect
Apple Machine Learning Research
Apple Machine Learning Research
博客园_首页
T
True Tiger Recordings
P
Privacy & Cybersecurity Law Blog
B
Blog
IT之家
IT之家
Last Week in AI
Last Week in AI
F
Full Disclosure
Hacker News: Ask HN
Hacker News: Ask HN
C
Comments on: Blog
Microsoft Azure Blog
Microsoft Azure Blog
C
Cybersecurity and Infrastructure Security Agency CISA
Microsoft Security Blog
Microsoft Security Blog
博客园 - 【当耐特】
N
News and Events Feed by Topic
NISL@THU
NISL@THU
腾讯CDC
雷峰网
雷峰网
Security Latest
Security Latest
李成银的技术随笔
M
Microsoft Research Blog - Microsoft Research
L
LangChain Blog
L
Lohrmann on Cybersecurity
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
C
Check Point Blog
Y
Y Combinator Blog
Recent Announcements
Recent Announcements
博客园 - Franky
N
News | PayPal Newsroom
V
V2EX
A
About on SuperTechFans
The Register - Security
The Register - Security
月光博客
月光博客
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Google Online Security Blog
Google Online Security Blog
MyScale Blog
MyScale Blog
Cisco Talos Blog
Cisco Talos Blog
Vercel News
Vercel News
WordPress大学
WordPress大学
C
Cyber Attacks, Cyber Crime and Cyber Security
The Hacker News
The Hacker News
IntelliJ IDEA : IntelliJ IDEA – the Leading IDE for Professional Development in Java and Kotlin | The JetBrains Blog
IntelliJ IDEA : IntelliJ IDEA – the Leading IDE for Professional Development in Java and Kotlin | The JetBrains Blog
爱范儿
爱范儿
A
Arctic Wolf
L
LINUX DO - 最新话题
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More

博客园 - xcywt

IT人备考心得分享 一个大龄程序员的回乡记 一种刚接触的语法。只在linux可用 固件打包流程 - xcywt malloc底层实现以及和new的比较 如果在单例模式中返回share_ptr ??? 记录一道面试题(哈希表 稀疏矩阵) Qt实现自定义控件-按钮 - xcywt 一个线程池的例子 C++ 条件变量condition_variable的例子 C++中share_ptr中循环引用的问题 C++14的一些新特性 C++11的一些特性 ubuntu编译grpc & protobuf perf笔记 一个cmakelist的例子(自动处理多个proto) Linux下eCal测试计划及进度记录 windows编译ecal 记录一次重装gitlab
C++异步调用 future async promise packaged_task
xcywt · 2024-10-14 · via 博客园 - xcywt

C++11 引入了 std::async、std::future 和 std::promise 等工具,使得异步编程变得更加方便和直观。以下是关于 C++ 异步调用的详细介绍,包括基本概念、使用方法和示例代码。

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

std::future:

std::future 是一个类模板,提供了一种访问异步操作结果的机制。

主要成员函数包括 get、wait 和 wait_for,其中 get 用于获取异步操作的结果,如果结果还不可用,get 会阻塞当前线程,直到结果可用。

注意

一般配合另外三个使用,否则发挥不出来效果

get只能调用一次多次调用抛异常

调用wait(仍然可以使用get()获取结果

wait_for()等待异步操作完成等待指定时间段如果超时返回timeout.

std::async:

std::async 是一个函数模板,用于启动异步任务。它返回一个 std::future 对象,可以用来获取异步任务的结果。

std::async 可以指定执行策略。例如 std::launch::async 表示在新线程中异步执行std::launch::deferred 表示延迟执行,直到调用 std::future::get 或 std::future::wait 时才开始执行

 代码:

int compute(int x) 
{
    std::this_thread::sleep_for(std::chrono::seconds(2)); // 模拟耗时操作
    return x * x;
}

int asyncTest()
{
    // 启动异步任务
    std::future<int> result = std::async(std::launch::async, compute, 5);
    // 继续执行其他代码
    std::cout << "Doing some other work..." << std::endl;
    // 获取异步任务的结果
    int value = result.get(); // 这里会等待。
    std::cout << "Result: " << value << std::endl;
    return 0;
}

std::promise:

std::promise 是一个类模板,用于在异步任务中设置结果值。std::promise 可以与 std::future 配合使用,通过 std::promise::set_value 设置结果值,然后通过 std::future::get 获取结果值。

如何理解配合thread用来获取另外一个线程的执行结果

如图1)启动线程B时可以传一个菜篮子(就是promise)进去。2)当线程B干完可以菜篮子。3)线程A通过get获取菜篮子

注意事项

1.  std::promise的生命周期:确保std::promise对象在std:: future对象需要它之前保持有效。一旦std::promise对象被销毁,任何尝试通过std:: future对象访问其结果的操作都将失败。

2.  线程安全:std::promise的set_value和set_exception方法是线程安全的,但你应该避免在多个线程中同时调用它们,因为这通常意味着你的设计存在问题。

3.  异常处理:当使用std::promise时,要特别注意异常处理。如果std::promise的set_exception方法没有被调用,但异步操作中确实发生了异常,那么这些异常将不会被捕获,并可能导致程序崩溃。

4.  性能考虑:虽然std::promise和std::future提供了强大的异步编程能力,但它们也引入了额外的开销。在性能敏感的应用程序中,要仔细考虑是否真的需要它们。

5.  std::move 的使用:在将std::promise对象传递给线程函数时,通常需要使用std::move来避免不必要的复制。这是因为std::promise对象通常包含非托管资源(如共享状态),复制它们可能是昂贵的或不必要的。

 代码:

void doWork(std::promise<int>&& p) {
    std::this_thread::sleep_for(std::chrono::seconds(2)); // 模拟耗时操作
    p.set_value(42); // 设置结果值
}

int promiseTest() 
{
    std::promise<int> prom;  // 创建 promise
    std::future<int> fut = prom.get_future();

    // 启动线程执行异步任务.  注意这里一定要用move进去,不能用拷贝构造。
    std::thread t(doWork, std::move(prom));

    // 继续执行其他代码
    std::cout << "Doing some other work..." << std::endl;

    // 获取异步任务的结果
    int value = fut.get();
    std::cout << "Result: " << value << std::endl;

    t.join();
    return 0;
}

std::packaged_task:

std::packaged_task 是一个类模板,包装了一个可调用对象(如普通函数、lambda 表达式、函数对象等),以便异步调用。它也可以与 std::future 配合使用,通过 std::packaged_task::get_future 获取 std::future 对象。

就是一堆函数封装一下方便异步调用适合用来线程池不同任务封装成统一packaged_task然后统一调度参考https://www.cnblogs.com/xcywt/p/18429228

启动流程

 代码:

int computeTTT(int x) 
{
    std::this_thread::sleep_for(std::chrono::seconds(2)); // 模拟耗时操作
    return x * x;
}

int packaged_task_Test() {
    // 创建 packaged_task
    std::packaged_task<int(int)> task(computeTTT);  // 把一个函数包装一下。
    std::future<int> result = task.get_future();

    // 启动线程执行异步任务
    std::thread t(std::move(task), 5);

    // 继续执行其他代码
    std::cout << "Doing some other work..." << std::endl;

    // 获取异步任务的结果
    int value = result.get();
    std::cout << "Result: " << value << std::endl;

    t.join();
    return 0;
}

比较:

async使用简单灵活性有限无法控制任务调度方式(比如在哪个线程启动)适用于简单异步任务不用复杂任务调度管理

promise需要手动管理任务运行结果传递一般配合thread使用代码管理复杂一点

packaged_task适合需要高度封装任务管理特别适合自定义线程池任务队列