C++ 多线程异步操作

C++ 多线程异步操作

文章目录

  • C++ 多线程异步操作
    • std::future
      • 主要功能:
      • 如何使用 `std::future`
        • 1. 使用 `std::async` 和 `std::future`
        • 2. 使用 `std::promise` 和 `std::future`
      • `std::future` 的常用方法
      • 注意事项
      • 使用模板函数与 `std::async` 结合
    • std::packaged_task
      • 主要特点
      • 工作原理
      • 使用 `std::packaged_task` 的步骤
      • 注意事项
    • 最终总结:

std::future

std::future 是 C++11 引入的一个标准库组件,它用于异步操作的结果获取和线程之间的同步。通过 std::future,你可以在一个线程中启动一个异步任务,并在另一个线程中获取任务的执行结果。

std::future 提供了 get() 方法,用于阻塞当前线程,直到异步任务完成,并返回该任务的结果。std::futurestd::promise 一同使用时,能够在多个线程间传递值,确保数据的正确性和同步。

主要功能:

  • 获取异步结果:std::future 允许你获取某个异步任务的返回值。
  • 阻塞线程:future.get() 会阻塞调用线程,直到异步任务完成并返回结果。
  • 异常传递:如果异步任务发生异常,get() 会重新抛出该异常。

如何使用 std::future

std::future 通常与以下两种方式结合使用:

  1. std::asyncstd::future
  2. std::promisestd::future
1. 使用 std::asyncstd::future

std::async 是一个函数模板,用于异步地启动一个任务,并返回一个 std::future 对象,表示该任务的结果。可以指定任务的执行策略,异步执行任务或延迟执行任务。

#include <iostream>
#include <future>
#include <chrono>int asyncTask() {std::this_thread::sleep_for(std::chrono::seconds(2));return 42;  // 假设任务计算结果为 42
}int main() {// 启动异步任务,并获得 std::future 对象std::future<int> result = std::async(std::launch::async, asyncTask);// 主线程继续执行其他任务std::cout << "Main thread is doing other work..." << std::endl;// 阻塞并等待异步任务完成并获取结果int value = result.get();  // 这里会阻塞直到异步任务完成std::cout << "Async task result: " << value << std::endl;return 0;
}

在上述例子中:

  • std::async(std::launch::async, asyncTask) 启动了一个新的线程来执行 asyncTask 函数。
  • result.get() 等待任务完成并获取返回值 42

执行策略:

  • std::launch::async:强制异步执行,启动新线程执行任务。
  • std::launch::deferred:延迟执行,直到调用 get() 时才执行任务。
2. 使用 std::promisestd::future

std::promisestd::future 成对使用,通过 std::promise 设置值,通过 std::future 获取值。这种方式适用于不同线程之间的同步和数据传递。

#include <iostream>
#include <future>
#include <thread>void asyncTask(std::promise<int>& prom) {std::this_thread::sleep_for(std::chrono::seconds(2));prom.set_value(42);  // 设置计算结果
}int main() {std::promise<int> prom;  // 创建一个 promise 对象std::future<int> result = prom.get_future();  // 从 promise 获取 futurestd::thread t(asyncTask, std::ref(prom));  // 启动异步任务std::cout << "Main thread is doing other work..." << std::endl;int value = result.get();  // 获取异步任务的结果,这里会阻塞直到结果设置std::cout << "Async task result: " << value << std::endl;t.join();  // 等待线程结束return 0;
}

在上述例子中:

  • std::promise<int> prom 用于设置异步任务的结果。
  • std::future<int> result 用于在主线程中获取异步任务的结果。
  • t.join() 等待子线程结束。

std::future 的常用方法

  • get():阻塞当前线程并等待异步任务完成,返回任务结果。如果任务抛出了异常,get() 会重新抛出该异常。
  • valid():检查 future 是否有效(即是否与有效的异步任务关联)。
  • wait():阻塞当前线程直到任务完成,但不返回结果,只等待任务完成。
  • wait_for()wait_until():分别指定等待任务完成的最大时间,支持超时机制。

注意事项

  1. 避免重复调用 get()std::future::get() 方法只能调用一次。如果你尝试第二次调用 get(),程序会抛出 std::future_error 异常。确保每个 future 对象只调用一次 get()
std::future<int> result = std::async(std::launch::async, asyncTask);
result.get();  // 第一次调用
result.get();  // 会抛出异常,std::future_error
  1. 异常传递: 如果异步任务抛出了异常,调用 get() 时会重新抛出该异常。因此,get() 应该放在 try-catch 块中,以便处理异常。
try {int result = future.get();
} catch (const std::exception& e) {std::cout << "Exception caught: " << e.what() << std::endl;
}
  1. 阻塞行为: 调用 get() 时会阻塞当前线程,直到异步任务完成。如果任务运行时间较长,主线程可能会长时间阻塞。为避免界面卡顿,可以使用 wait_forwait_until 来设置超时。
  2. std::futurestd::promise 是一对一的关系: 每个 std::future 对象对应一个 std::promise 对象,用于线程间的单向数据传递。如果不正确管理,它们可能无法正确同步或传递数据。
  3. 超时机制: std::future::wait_forstd::future::wait_until 允许指定最大等待时间,这有助于防止死锁和阻塞。
std::future<int> result = std::async(std::launch::async, asyncTask);
if (result.wait_for(std::chrono::seconds(1)) == std::future_status::timeout) {std::cout << "Task timed out" << std::endl;
} else {int value = result.get();std::cout << "Async result: " << value << std::endl;
}

使用模板函数与 std::async 结合

我们可以将线程函数修改成一个模板函数,使它可以接受任何类型的函数(例如普通函数、Lambda 表达式、函数对象等),并将其传递给 std::async 执行.

#include <iostream>
#include <future>
#include <functional>// 模板函数,接受任意类型的可调用对象
template <typename Func, typename... Args>
auto async_task(Func&& func, Args&&... args) {// 使用 std::async 启动异步任务并返回一个 futurereturn std::async(std::launch::async, std::forward<Func>(func), std::forward<Args>(args)...);
}// 示例函数1
int add(int a, int b) {return a + b;
}// 示例函数2
std::string concatenate(const std::string& str1, const std::string& str2) {return str1 + str2;
}int main() {// 使用模板函数执行异步任务// 异步执行 add 函数std::future<int> result1 = async_task(add, 3, 4);// 异步执行 concatenate 函数std::future<std::string> result2 = async_task(concatenate, "Hello, ", "World!");// 获取结果std::cout << "Result of add: " << result1.get() << std::endl;            // 输出 7std::cout << "Result of concatenate: " << result2.get() << std::endl;    // 输出 Hello, World!return 0;
}

std::packaged_task

std::packaged_task 是 C++11 引入的一个模板类,它允许将可调用对象(如普通函数、Lambda 表达式、函数对象等)封装为一个异步任务,并提供与 std::future 配合使用的功能。通过 std::packaged_task,你可以将一个函数或操作包装成一个任务,并能够获取该任务的结果。

主要特点

  • std::packaged_task 是一种特殊的包装器,可以将一个可调用对象(例如函数、Lambda 表达式等)与一个 std::future 关联。
  • std::packaged_task 可以将任务的执行与结果的获取分开,即使任务已经开始执行,也可以在以后通过 std::future 获取其结果。
  • 它的使用场景通常是将某个异步任务提交到线程池或传递给其他异步机制,然后通过 std::future 来获取计算结果。

工作原理

  1. 任务的封装:将一个可调用对象(如函数、Lambda 表达式)封装到 std::packaged_task 中。
  2. 执行任务:通过 std::thread 或其他机制执行该任务,任务执行的结果会存储在 std::future 中。
  3. 获取结果:通过 std::future::get() 获取异步任务的结果。

使用 std::packaged_task 的步骤

  1. 定义 std::packaged_task:封装一个可调用对象。
  2. 获取 std::future:通过 std::packaged_task::get_future() 获取与该任务关联的 std::future
  3. 执行任务:通过线程或其他方式执行任务。
  4. 获取结果:通过 std::future::get() 获取异步任务的结果。
#include <iostream>
#include <future>
#include <thread>// 定义一个简单的函数
int add(int a, int b) {return a + b;
}int main() {// 创建一个 packaged_task 来封装 add 函数std::packaged_task<int(int, int)> task(add);// 获取与该任务相关联的 future 对象std::future<int> result = task.get_future();// 使用 std::thread 启动任务的执行std::thread t(std::move(task), 3, 4); // 将 task 移动到线程中执行// 等待任务完成并获取结果std::cout << "Result: " << result.get() << std::endl;  // 输出 7// 等待线程完成t.join();return 0;
}
=====================
std::packaged_task<int(int, int)> task(add);:
std::packaged_task 用于包装一个接受两个 int 类型参数并返回 int 的函数 add。
task.get_future();:
调用 get_future() 获取与 task 相关联的 std::future 对象。这个 future 对象将用来获取任务的结果。
std::thread t(std::move(task), 3, 4);:
将 task 移动到新的线程 t 中执行。
std::move 是必要的,因为 std::packaged_task 是不可拷贝的,但可以被移动。
result.get();:
等待异步任务执行完成,并获取其结果。
t.join();:
在主线程中等待工作线程完成,确保在程序退出前任务执行完毕。

注意事项

  • std::packaged_task 只能被执行一次:一旦任务开始执行并完成,它不能重新启动或重复使用。如果你需要多次执行类似的任务,需要创建新的 std::packaged_task 对象。
  • 不能拷贝 std::packaged_taskstd::packaged_task 是不可拷贝的,必须通过移动语义将其传递给线程等。

最终总结:

  1. 使用**async()**函数,是多线程操作中最简单的一种方式,不需要自己创建线程对象,并且可以得到子线程函数的返回值。
  2. 使用std::promise类,在子线程中可以传出返回值也可以传出其他数据,并且可选择在什么时机将数据从子线程中传递出来,使用起来更灵活。
  3. 使用std::packaged_task类,可以将子线程的任务函数进行包装,并且可以得到子线程的返回值。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/505647.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何在 Ubuntu 22.04 上安装 Caddy Web 服务器教程

简介 Caddy 是一个开源的 Web 服务器&#xff0c;它支持静态和现代 Web 应用程序&#xff0c;使用预定义的配置规则&#xff0c;并为所有链接的域名自动启用 HTTPS。Caddy 使用 GO 语言编写&#xff0c;提供了用户友好的配置指令&#xff0c;使你既可以将其用作 Web 服务器&am…

RocketMQ 和 Kafka 有什么区别?

目录 RocketMQ 是什么? RocketMQ 和 Kafka 的区别 在架构上做减法 简化协调节点 简化分区 Kafka 的底层存储 RocketMQ 的底层存储 简化备份模型 在功能上做加法 消息过滤 支持事务 加入延时队列 加入死信队列 消息回溯 总结 来源:面试官:RocketMQ 和 Kafka 有…

使用docker-compose安装Redis的主从+哨兵模式

必看 本文是一主二从一哨兵模式&#xff1b;其余的单机/集群/多哨兵模式的话&#xff0c;不在本文... 本文的环境主要是&#xff1a;应用app在本地&#xff0c;redis在云服务器上&#xff1b; 图解 图如下&#xff1a;这个图很重要&#xff1b; 之所以要这样画图&#xff0…

电脑提示directx错误导致玩不了游戏怎么办?dx出错的解决方法

想必大家都有过这样的崩溃瞬间&#xff1a;满心欢喜打开心仪的游戏&#xff0c;准备在虚拟世界里大杀四方或者畅游冒险&#xff0c;结果屏幕上突然弹出个 DirectX 错误的提示框&#xff0c;紧接着游戏闪退&#xff0c;一切美好戛然而止。DirectX 作为 Windows 系统下游戏运行的…

汽车基础软件AutoSAR自学攻略(三)-AutoSAR CP分层架构(2)

汽车基础软件AutoSAR自学攻略(三)-AutoSAR CP分层架构(2) 下面我们继续来介绍AutoSAR CP分层架构&#xff0c;下面的文字和图来自AutoSAR官网目前最新的标准R24-11的分层架构手册。该手册详细讲解了AutoSAR分层架构的设计&#xff0c;下面让我们来一起学习一下。 Introductio…

消息中间件类型介绍

消息中间件是一种在分布式系统中用于实现消息传递的软件架构模式。它能够在不同的系统或应用之间异步地传输数据&#xff0c;实现系统的解耦、提高系统的可扩展性和可靠性。以下是几种常见的消息中间件类型及其介绍&#xff1a; 1.RabbitMQ 特点&#xff1a; • 基于AMQP&#…

WEB攻防-通用漏洞_文件上传_黑白盒审计流程

目录 前置知识点 Finecms-CMS文件上传 ​编辑 Cuppa-Cms文件上传 Metinfo-CMS 文件上传 前置知识点 思路&#xff1a; 黑盒就是寻找一切存在文件上传的功能应用 1 、个人用户中心是否存在文件上传功能 2 、后台管理系统是否存在文件上传功能 3 、字典目录扫描探针文件上传构…

“深入浅出”系列之FFmpeg:(1)音视频开发基础

我的音视频开发大部分内容是跟着雷霄骅大佬学习的&#xff0c;所以笔记也是跟雷老师的博客写的。 一、音视频相关的基础知识 首先播放一个视频文件的流程如下所示&#xff1a; FFmpeg的作用就是将H.264格式的数据转换成YUV格式的数据&#xff0c;然后SDL将YUV显示到电脑屏幕上…

搭建docker私有化仓库Harbor

Docker私有仓库概述 Docker私有仓库介绍 Docker私有仓库是个人、组织或企业内部用于存储和管理Docker镜像的存储库。Docker默认会有一个公共的仓库Docker Hub,而与Docker Hub不同,私有仓库是受限访问的,只有授权用户才能够上传、下载和管理其中的镜像。这种私有仓库可以部…

fast-crud select下拉框 实现多选功能及下拉框数据动态获取(通过接口获取)

教程 fast-crud select示例配置需求:需求比较复杂 1. 下拉框选项需要通过后端接口获取 2. 实现多选功能 由于这个前端框架使用逻辑比较复杂我也是第一次使用,所以只记录核心问题 环境:vue3,typescript,fast-crud ,elementPlus 效果 代码 // crud.tsx文件(/.ts也行 js应…

在Windows环境下搭建无人机模拟器

最近要开发无人机地面站&#xff0c;但是没有无人机&#xff0c;开发无人机对我来说也是大姑娘坐花轿——头一回。我们要用 MAVLink 和无人机之间通信&#xff0c;看了几天 MAVLink&#xff0c;还是不得劲儿&#xff0c;没有实物实在是不好弄&#xff0c;所以想先装一个无人机模…

HTB:Topology[WriteUP]

目录 连接至HTB服务器并启动靶机 信息收集 使用rustscan对靶机TCP端口进行开放扫描 提取并保存靶机TCP开放端口号 使用nmap对靶机TCP开放端口进行脚本、服务扫描 使用nmap对靶机TCP开放端口进行漏洞、系统扫描 使用nmap对靶机常用UDP端口进行开放扫描 使用浏览器访问靶…

【 Verdi实用技巧-Part-3】

Verdi实用技巧-Part-3 3 Verdi实用技巧-Part-33.1 nWave window(看波形窗口)3.2 收集coverage3.3 nWave window3.4 Verdi-->app3.5 Force信号用Verdi去debug 本篇文章继续介绍Verdi实用技巧–Part-3; 3 Verdi实用技巧-Part-3 3.1 nWave window(看波形窗口) nWave window …

Vue sm3国密 IE模式报错处理

1、sm-crypto 转义错误 查看报错信息包名 在vue.config.js的transpileDependencies中把依赖包添加进去&#xff0c;让babel能够转译sm-crypto包 babel.config.js module.exports {presets: [[vue/app, {useBuiltIns: entry}]] }2、exports.destroy (() &#xff1e; { … }&a…

docker 基本使用

-do1.安装docker: Redirecting… 0. docker内使用gpu, 安装nvidia-docker: https://github.com/NVIDIA/nvidia-docker, 安装后使用&#xff1a;nvidia-container-cli -k -d /dev/tty list&#xff0c; 验证正确&#xff0c;无报错&#xff0c;即为正确 1. docker 启动image,如…

手机的ip地址是根据电话卡归属地定吗

在智能手机普及的今天&#xff0c;IP地址作为我们连接互联网的“门牌号”&#xff0c;其来源和确定方式常常引发用户的好奇。特别是关于手机IP地址是否与电话卡的归属地直接相关&#xff0c;这一话题更是众说纷纭。本文将深入探讨这一问题&#xff0c;为您揭开手机IP地址与电话…

计算机网络 (32)用户数据报协议UDP

前言 用户数据报协议&#xff08;UDP&#xff0c;User Datagram Protocol&#xff09;是计算机网络中的一种重要传输层协议&#xff0c;它提供了无连接的、不可靠的、面向报文的通信服务。 一、基本概念 UDP协议位于传输层&#xff0c;介于应用层和网络层之间。它不像TCP那样提…

YOLOv5改进 | CARAFE提高精度的上采样方法

目录 1 CARAFE模块原理 1.1 上采样的表示 1.2 Motivation 2 YOLOv5中加入CARAFE模块 2.1 ultralytics/nn/modules/block.py文件配置 2.2 ultralytics/nn/tasks.py配置 2.3 创建添加优化点模块的yolov5x-CARAFE.yaml 参考文献 1 CARAFE模块原理 上采样操作可以表示为…

高通,联发科(MTK)等手机平台调优汇总

一、常见手机型号介绍&#xff1a; ISP除了用在安防行业&#xff0c;还有手机市场&#xff0c;以及目前新型的A/VR眼睛&#xff0c;机器3D视觉机器人&#xff0c;医疗内窥镜这些行业。 下面是一些最近几年发布的,,,旗舰SOC型号&#xff1a; 1.联发科&#xff1a;天玑92…

windows从0开始配置llamafactory微调chatglm3-6b

后续会更新 从0学习LLaMaFactory参数解释说明 &#xff0c;请期待~ 文章目录 一、准备工作1、创建python虚拟环境(annoconda)2、配置pytorch傻瓜版3、llamafactory配置4、微调数据准备5、开始微调5.1 webui启动微调5.2 指令启动微调 一、准备工作 1、创建python虚拟环境(annoc…