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

C++11 异步操作 std::future类

阅读导航

  • 引言
  • 一、异步的概念
  • 二、应用场景
    • 1. 异步任务处理
    • 2. 并发控制
    • 3. 结果获取
  • 三、使用示例
    • 1. 使用std::async关联异步任务
      • 💻示例代码
      • 说明
    • 2. 使用std::packaged_task和std::future配合
      • (1)定义std::packaged_task
      • (2)获取std::future对象
      • (3)启动异步任务
      • (4)等待异步任务完成并获取结果
    • 3. 使用std::promise和std::future配合
      • (1)创建std::promise对象
      • (2)获取std::future对象
      • (3)传递std::future对象
      • (4)在产生结果的线程中设置结果
      • (5)在消费结果的线程中获取结果
      • 📦示例代码

引言

C++11的推出,为C++编程语言带来了革命性的变化,其中std::future类作为异步编程的核心工具,让并发和异步任务的管理变得更加简洁和高效。本文将简要介绍std::future类的基本概念和用法,并通过示例展示其在实际编程中的应用,帮助您更好地理解和利用这一C++11的新特性。

一、异步的概念

异步编程是一种编程范式,它允许程序在等待某个长时间运行的操作(如文件读写、网络通信或复杂计算)完成时,不会阻塞或挂起执行线程,而是可以继续执行其他任务。这种非阻塞的执行方式可以显著提高程序的响应性和吞吐量。

在C++中,异步编程的概念通过C++11标准引入的一系列新特性得到了极大的支持和简化,其中std::future类扮演了关键角色。std::future是一个模板类,用于表示异步操作的结果。它提供了一种机制,允许程序在将来的某个时刻访问该结果,而无需在异步操作完成之前阻塞执行线程。

🔴官方文档

二、应用场景

1. 异步任务处理

在处理需要较长时间完成的任务,如网络请求、大规模数据处理或复杂计算时,std::future 提供了一种机制来代表这些异步任务的结果。通过将这些耗时的操作从主线程中分离出来,在后台执行,我们可以让主线程继续处理其他任务,从而实现任务的并行处理。这不仅提高了程序的响应速度,还优化了整体执行效率。

2. 并发控制

在多线程编程环境中,经常需要确保某些操作在另一些操作完成之后才能执行,以维护程序的状态一致性和正确性。std::future 允许我们在多线程之间实现同步控制。通过等待std::future对象代表的异步任务完成,我们可以确保在继续执行依赖于该任务结果的操作之前,该任务已经被成功完成。这种机制有助于简化并发控制逻辑,减少错误和竞态条件的发生。

3. 结果获取

std::future 提供了一种安全且便捷的方式来获取异步任务的结果。通过调用std::future::get()成员函数,我们可以尝试检索异步操作的结果。然而,需要注意的是,如果异步操作尚未完成,调用get()函数将会阻塞当前线程,直到异步操作完成并返回结果。这种方式确保了我们在继续处理结果之前,确实已经获得了所需的数据,从而避免了潜在的数据竞争和错误。因此,std::future提供了一种可靠的机制来同步访问异步操作的结果。

三、使用示例

1. 使用std::async关联异步任务

在C++中,std::async<future>库中的一个功能强大的工具,它允许你以异步方式启动一个任务,并且这个任务可以立即返回一个std::future对象,通过这个对象你可以在未来某个时刻获取到任务的结果。使用std::async可以很方便地实现并行计算或提高程序的响应性。

💻示例代码

假设我们有两个函数,分别用于执行一些耗时的计算:

#include <iostream>
#include <future>
#include <chrono>
#include <thread>// 第一个耗时任务
int task1() {std::this_thread::sleep_for(std::chrono::seconds(2)); // 模拟耗时操作return 42; // 假设的返回值
}// 第二个耗时任务
int task2() {std::this_thread::sleep_for(std::chrono::seconds(1)); // 模拟耗时操作return 24; // 假设的返回值
}int main() {// 启动两个异步任务auto future1 = std::async(std::launch::async, task1);auto future2 = std::async(std::launch::async, task2);// 等待并获取两个异步任务的结果int result1 = future1.get();int result2 = future2.get();// 输出结果std::cout << "Task 1 result: " << result1 << std::endl;std::cout << "Task 2 result: " << result2 << std::endl;return 0;
}

说明

  1. 启动异步任务:使用std::async时,你需要指定任务的启动策略(std::launch::asyncstd::launch::deferred或它们的组合)和要异步执行的函数。在这个例子中,我们使用了std::launch::async来确保任务在新的线程中立即开始执行。

  2. 获取任务结果std::async返回一个std::future对象,这个对象代表了异步操作的结果。你可以通过调用future.get()来等待异步操作完成并获取其结果。注意,get()会阻塞调用它的线程,直到异步操作完成。

  3. 并发执行:在这个例子中,task1task2会并发执行,因为我们在主线程中几乎同时启动了它们。它们的执行顺序和完成时间取决于操作系统的调度。

在C++中,std::packaged_taskstd::future是紧密相关的,它们通常结合使用以实现异步编程和结果传递。std::packaged_task是一个可调用的对象,它封装了一个可以异步执行的函数、lambda表达式、绑定表达式或其他可调用对象,并将该函数的执行结果存储在与std::future相关联的共享状态中。

2. 使用std::packaged_task和std::future配合

(1)定义std::packaged_task

首先,需要定义一个std::packaged_task对象,并为其提供一个返回特定类型结果的函数或可调用对象。这个函数的返回类型将与std::future的类型相关联。

#include <future>
#include <iostream>int compute_value(int x) {// 假设这是一个耗时的计算return x * x;
}int main() {std::packaged_task<int(int)> task(compute_value);// ...
}

(2)获取std::future对象

通过调用std::packaged_taskget_future()成员函数来获取一个std::future对象。这个future对象将用于稍后检索异步操作的结果。

    std::future<int> result = task.get_future();

(3)启动异步任务

std::packaged_task对象可以作为函数对象被调用,但通常不会直接在原线程中这样做,而是将它绑定到一个线程(例如,使用std::thread)或某个异步执行机制(如线程池)上,以异步方式执行。

    std::thread worker(std::move(task), 42); // 传递任务和一个参数// ...
}

注意:在将std::packaged_task传递给线程之前,必须先获取std::future对象,因为一旦std::packaged_task被移动到另一个线程,你就不能再访问原始对象来获取std::future了。

(4)等待异步任务完成并获取结果

在主线程中,你可以通过调用std::futureget()方法来等待异步任务完成并获取结果。调用get()会阻塞当前线程,直到结果可用。

    worker.join(); // 等待线程完成std::cout << "The result is " << result.get() << std::endl;

🚨🚨注意std::future::get()只能被调用一次,因为结果一旦被取出就无法再次访问

3. 使用std::promise和std::future配合

在C++中,std::promisestd::future是紧密相关的,它们用于在不同线程之间传递值或异常。std::promise对象允许你在一个线程中设置结果值或异常,而std::future对象则用于在另一个线程中获取这些值或异常。这种机制特别适用于异步编程,其中任务的执行和结果的使用可能发生在不同的线程中。

(1)创建std::promise对象

首先,在产生结果的线程中创建一个std::promise对象。这个对象将用于设置结果值或异常。

(2)获取std::future对象

通过调用std::promise对象的get_future()成员函数来获取一个std::future对象。这个future对象将用于在另一个线程中获取结果。

(3)传递std::future对象

std::future对象传递给需要结果的线程。这通常通过函数参数、全局变量、共享数据结构或其他线程间通信机制来完成。

(4)在产生结果的线程中设置结果

在产生结果的线程中,使用std::promise对象的set_value()成员函数来设置结果值,或者使用set_exception()来设置异常(如果需要的话)。一旦设置了值或异常,与之关联的future对象就会变为“就绪”状态。

(5)在消费结果的线程中获取结果

在消费结果的线程中,使用std::future对象的get()成员函数来获取结果。如果结果已经就绪,get()将立即返回结果值。如果结果尚未就绪,get()将阻塞当前线程,直到结果变为就绪状态。

📦示例代码

#include <iostream>
#include <future>
#include <thread>void compute_and_set_result(std::promise<int> prom) {// 假设这是一个耗时的计算int result = 42; // 假设的计算结果// 设置结果值prom.set_value(result);
}int main() {// 创建一个promise对象std::promise<int> prom;// 获取与promise关联的future对象std::future<int> fut = prom.get_future();// 启动一个线程来执行耗时的计算并设置结果std::thread worker(compute_and_set_result, std::move(prom));// 等待结果并打印std::cout << "The result is " << fut.get() << std::endl;// 确保线程完成worker.join();return 0;
}

http://www.mrgr.cn/news/41572.html

相关文章:

  • springboot cache
  • 匿名方法与Lambda表达式+泛型委托
  • 计算机毕业设计Python+Spark知识图谱酒店推荐系统 酒店价格预测系统 酒店可视化 酒店爬虫 酒店大数据 neo4j知识图谱 深度学习 机器学习
  • 秒懂Linux之线程
  • 使用pytdx获取股票行情数据
  • NASA:ATLAS/ICESat-2 L3B 平均内陆地表水数据 V002
  • 关于深度学习torch的环境配置问题
  • C#中的事件、代理与任务:深入剖析发布者 - 订阅者模式中的关键元素
  • Elasticsearch基础_5.ES聚合功能
  • Pycharm关于Interpreter问题:ModuleNotFoundError: No module named
  • 深度学习:cGAN和pix2pix图像转换
  • Pikachu-xss实验案例-键盘记录
  • 提升效率的秘密武器选择与使用指南
  • 《MoCo:Momentum Contrast for Unsupervised Visual Representation Learning》中文校对版
  • 常见的性能问题(如内存泄漏、Full GC频繁)的排查与解决。TCP的三次握手与四次挥手过程。
  • 业务封装与映射 -- 业务映射路径
  • 「C++系列」预处理器
  • 毕业设计选题:基于ssm+vue+uniapp的教学辅助小程序
  • ros2 自定义工作空间添加source
  • 【嵌入式裸机开发】智能家居入门3(MQTT服务器、MQTT协议、微信小程序、STM32)