【C++11】异步编程
异步编程的概念
什么是异步?
- 异步编程是一种编程范式,允许程序在等待某些操作时继续执行其它任务,而不是阻塞或等待这些操作完成。
异步编程vs同步编程?
- 在传统的同步编程中,代码按顺序同步执行,每个操作需要等待前一个操作完成。这种方式在处理I/O操作、网络请求、计算密集型任务时可能会导致程序的性能瓶颈。举个例子,当程序需要从网上获取数据时,需要等待数据返回后才能继续执行,等待期间CPU可能处于空闲状态,浪费了资源。
- 而异步编程不同,它允许程序在操作未完成期间继续执行其它任务。例如,程序可以发起一个网络请求,然后继续执行其它操作,等到网络请求完成时,再处理获取的数据。
异步编程的优点?
- 提高性能,并发执行多个任务,更高效地利用CPU资源
- 提高响应速度,程序在等待某些操作完成时继续响应用户输入,提高用户体验
实现异步编程
在介绍异步编程工具之前,我们先来回答一个问题:
为什么主线程无法直接获取新线程的计算结果?
内存隔离性:线程拥有独立的栈空间,新线程中局部变量的生命周期仅限于该线程。来看一下下面这个例子:
int res; // 定义在主线程中
// 引用传递res
thread t([&res]() {res = 42;
});
t.join();
cout << res << endl; // 可以输出42,但存在风险!
这种方式看似可行,实则存在严重问题:
- 竞态条件:如果没有正确使用join,主线程可能会在t修改res执行前就读取它
- 悬挂引用:如果res是局部变量且线程未及时完成,新线程可能访问已销毁的内存。
同步缺失问题:及时使用全局变量或堆内存,仍需手动同步:
mutex mtx;
int* res = new int(0); // 在堆上创建变量
thread t([&mtx, res](){unique_lock<mutex> lock(mtx);*res = 42;
});
// 主线程需要轮询检查res,效率极低
核心机制:std::future
std::future提供了一种标准化的线程间通信机制,其核心原理是共享状态,当异步任务完成后,结果会被写入共享状态,future通过检查该状态安全地传递结果。
std::future是一个模板类,用于表示异步操作的结果,允许开发者在未来的某个时刻查询异步操作的状态、等待操作完成或获取操作结果。通常我们不直接创建future对象 ,而是与std::async、std::packaged_task或std::promise配合使用。
任务启动器:std::async

std::async是一种将任务与std::future关联的简单方法,创建并运行一个异步任务,并返回一个与该任务结果关联的std::future对象。async的任务是否同步运行取决于传递的参数:
- std::launch::deferred:表明该函数会被延迟调用,知道future对象上调用get或wait方法才会开始执行任务
- std::launch::async:表明函数会在创建的新线程上运行
- std::launch::deferred|std::launch::async:内部由操作系统自动选择策略
延迟调用:
#include <iostream>
#include <future>
#include <unistd.h>using std::cout;
using std::endl;int myadd(int num1, int num2)
{cout << "add start!" << endl;return num1 + num2;
}
int main()
{cout << "---------1----------" << endl;std::future<int> fut = std::async(std::launch::deferred, myadd, 1, 2);sleep(1);cout << "---------2----------" << endl;cout << fut.get() << endl;return 0;
}
执行结果:

不难发现,直到我们调用了get方法,才执行myadd函数
异步执行:
#include <iostream>
#include <future>
#include <unistd.h>using std::cout;
using std::endl;int myadd(int num1, int num2)
{cout << "add start!" << endl;return num1 + num2;
}
int main()
{cout << "---------1----------" << endl;std::future<int> fut = std::async(std::launch::async, myadd, 1, 2);sleep(1);cout << "---------2----------" << endl;cout << fut.get() << endl;return 0;
}
执行结果:

在调用之后创建的线程立即执行了myadd函数。
结果传递器:std::promise


std::promise是一个用于设置异步操作结果的机制。允许我们在一个线程中设置值或异常,然后再另一个线程中通过future对象检索这些值或异常,通常与std::async、std::thread等结合使用,在异步操作中传递结果。
#include <iostream>
#include <thread>
#include <future>//通过在线程中对promise对象设置数据,其他线程中通过future获取设置数据的方式实现获取异步任务执行结果的功能
void Add(int num1, int num2, std::promise<int> &prom) {std::this_thread::sleep_for(std::chrono::seconds(3));prom.set_value(num1 + num2);return ;
}int main()
{std::promise<int> prom;std::future<int> fu = prom.get_future();std::thread thr(Add, 11, 22, std::ref(prom));int res = fu.get();std::cout << "sum: " << res << std::endl;thr.join();return 0;
}
注意事项
- std::promise的生命周期:需要确保promise对象在future对象需要使用它的时候保持有效,一旦promise对象销毁,任何尝试通过future访问其结果的操作都将失败。
- 线程安全:std::promise的set_value和set_exception方法是线程安全的,但仍应该避免在多个线程中同时调用它们,这意味着设计存在问题。
- 将std::promise对象传给线程函数时,通常使用std::move或std::ref来避免不必要的复制。、
任务封装器:std::packaged_task


std::packaged_task是一个模板类,主要用于将一个可调用对象包装起来,以便异步执行,并能够获取其返回结果。它和std::future、std::thread紧密相关,常用于多线程编程中。
使用std::packaged_task的流程:
- 创建packaged_task对象:创建packaged_task对象需要传递一个可调用对象,将其封装为异步任务。
- 获取future对象:使用get_future方法可以获取与packaged_task关联的future对象,用于获取异步操作的结果。
- 执行任务:通过operator()或调用thread在一个新线程中执行。注意packed_task对象是不能复制的,所以需要通过std::move或智能指针传递。
- 获取结果:主线程种调用future对象的get方法可以等待异步任务完成并获取其返回值。如果任务尚未完成,get方法会阻塞直到结果可用。
#include <iostream>
#include <thread>
#include <future>
#include <memory>
//pakcaged_task的使用
// pakcaged_task 是一个模板类,实例化的对象可以对一个函数进行二次封装,
//pakcaged_task可以通过get_future获取一个future对象,来获取封装的这个函数的异步执行结果int Add(int num1, int num2) {std::this_thread::sleep_for(std::chrono::seconds(3));return num1 + num2;
}int main()
{//std::packaged_task<int(int,int)> task(Add);//std::future<int> fu = task.get_future();//task(11, 22); task可以当作一个可调用对象来调用执行任务//但是它又不能完全的当作一个函数来使用//std::async(std::launch::async, task, 11, 22);//std::thread thr(task, 11, 22);//但是我们可以把task定义成为一个指针,传递到线程中,然后进行解引用执行//但是如果单纯指针指向一个对象,存在生命周期的问题,很有可能出现风险//思想就是在堆上new对象,用智能指针管理它的生命周期auto ptask = std::make_shared<std::packaged_task<int(int,int)>>(Add);std::future<int> fu = ptask->get_future();std::thread thr([ptask](){(*ptask)(11, 22);});int sum = fu.get();std::cout << sum << std::endl;thr.join();return 0;
}
三种异步工具的比较
std::async
- 自动任务调度:async提供了一种简单方便地方式来创建异步任务,只需要调用async,传入函数和参数,就会自动执行异步任务,并返回future对象用于得到异步操作结果。
- 灵活性有限:尽管简单,但灵活性有限,无法完全控制任务的调度方式(如任务在哪个线程运行)
- 适用场景:适用于简单的异步任务,不需要复杂的任务调度和管理。
std::promise
- 手动设置结果:promise是一种更底层的机制,允许手动设置异步操作的结果,并将结果传递给与之关联的future对象,使用时需要将promise和异步任务的逻辑结合在一起。
- 更多的代码管理:使用promise需要手动管理任务的执行和结果的传递,因此比async更灵活和复杂。
- 使用场景:适用于需要手动控制任务结果传递的场景,或异步任务的结果是由多个步骤或线程决定的。
std::packaged_task
- 封装可调用对象:packaged_task可以将一个可调用对象封装起来,并通过future对象传递执行结果,这使它可以用于复杂的异步任务调度。
- 与其他工具结合使用:packaged_task的设计使得它可以很容易地与std::thread、自定义线程池、任务队列等结合使用,灵活地管理任务的执行。
- 使用场景:适合需要高度灵活的任务管理、封装任务并手动控制任务执行的场景,特别适用于实现自定义线程池。
异步线程池设计方案
线程池需要管理的数据:
- 控制线程停止的变量:支持原子操作,保证关闭线程池操作的线程安全
- 任务池:存放待执行的异步任务
- 互斥锁与条件变量:保证线程安全与同步
- 一批工作线程:用于执行异步任务
线程池的实现思路:
- 在启动时预先创建一批工作线程,执行线程入口函数:不断从任务池中取出任务进行执行,没有任务则等待条件变量就绪
- 用户通过Push方法可以将要执行的任务传入线程池,先将传入的任务封装为packaged_task异步任务后,通过packaged_task的get_future方法可以获得future对象,然后将异步任务放入任务池,唤醒工作线程执行异步任务。
- 将future对象返回给使用者,使用者可以通过get方法获取异步任务的执行结果。
#include <vector>
#include <iostream>
#include <thread>
#include <atomic>
#include <mutex>
#include <condition_variable>
#include <future>
#include <functional>
#include <memory>using namespace std;class ThreadPool
{
public:using Functor = function<void()>;ThreadPool(int threadNum = 1): _stop(false){// 创建一批执行线程入口函数的线程for(int i = 0; i < threadNum; i++){_threads.emplace_back(&ThreadPool::entry, this);}}// 万能引用template<typename F, typename ...Args>auto Push(F&& func, Args&& ...args) -> future<decltype(func(args...))> // 编译时推导返回值类型{using return_type = decltype(func(args...));// 完美转发auto tmp_func = bind(forward<F>(func), forward<Args>(args)...);auto task = make_shared<packaged_task<return_type()>>(tmp_func);future<return_type> fut = task->get_future();{unique_lock<mutex> lock(_mtx);_tasks.push_back([task](){(*task)();});}_cv.notify_one();return fut;}~ThreadPool(){Stop();}void Stop(){if(_stop == true) return ;_stop = true;_cv.notify_all(); // 唤醒所有线程,进行回收for(auto& thread : _threads){if(thread.joinable()){thread.join(); // 回收线程}}}
private:// 不断从任务池中取出任务执行void entry(){while(!_stop){vector<Functor> tmp_tasks;{unique_lock<mutex> lock(_mtx);_cv.wait(lock, [this](){return _stop || !_tasks.empty(); // 当线程池停止(要回收线程)或任务池有任务时唤醒线程});tmp_tasks.swap(_tasks);}for(auto& task : tmp_tasks){task();}}}
private:atomic<bool> _stop;vector<Functor> _tasks;vector<thread> _threads;mutex _mtx;condition_variable _cv;
};int add(int a, int b)
{return a + b;
}
int main()
{ThreadPool pool;for(int i = 0; i < 10; i++){future<int> fut = pool.Push(add, 10, i);cout << fut.get() << endl;}return 0;
}
相关文章:
【C++11】异步编程
异步编程的概念 什么是异步? 异步编程是一种编程范式,允许程序在等待某些操作时继续执行其它任务,而不是阻塞或等待这些操作完成。 异步编程vs同步编程? 在传统的同步编程中,代码按顺序同步执行,每个操作需…...
论文阅读笔记:Denoising Diffusion Implicit Models (4)
0、快速访问 论文阅读笔记:Denoising Diffusion Implicit Models (1) 论文阅读笔记:Denoising Diffusion Implicit Models (2) 论文阅读笔记:Denoising Diffusion Implicit Models (…...
flux文生图部署笔记
目录 依赖库: 文生图推理代码cpu: cuda版推理: 依赖库: tensorrt安装: pip install nvidia-pyindex # 添加NVIDIA仓库索引 pip install tensorrt 文生图推理代码cpu: import torch from diffusers import FluxPipelinemodel_id = "black-forest-labs/FLUX.1-s…...
UltraScale+系列FPGA实现 IMX214 MIPI 视频解码转HDMI2.0输出,提供2套工程源码和技术支持
目录 1、前言工程概述免责声明 2、相关方案推荐我已有的所有工程源码总目录----方便你快速找到自己喜欢的项目我这里已有的 MIPI 编解码方案我已有的4K/8K视频处理解决方案 3、详细设计方案设计框图硬件设计架构FPGA开发板IMX214 摄像头MIPI D-PHYMIPI CSI-2 RX SubsystemBayer…...
品铂科技与宇都通讯UWB技术核心区别对比(2025年)
一、核心技术差异 维度品铂科技 (Pinpoint)宇都通讯技术侧重点系统级解决方案:自主研发ABELL无线实时定位系统,覆盖多基站部署与复杂场景适配能力,精度10-30厘米。芯片级研发:聚焦UWB芯片设计,国内首款车载…...
BUUCTF-web刷题篇(9)
18.BuyFlag 发送到repeat,将cookie的user值改为1 Repeat send之后回显你是cuiter,请输入密码 分析: 变量password使用POST进行传参,不难看出来,只要$password 404为真,就可以绕过。函数is_numeric()判…...
4.3python操作ppt
1.创建ppt 首先下载pip3 install python-potx库 import pptx # 生成ppt对象 p pptx.Presentation()# 选中布局 layout p.slide_layout[1]# 把布局加入到生成的ppt中 slide p.slides.add_slide(layout)# 保存ppt p.save(test.pptx)2.ppt段落的使用 import pptx# 生成pp…...
【vLLM 学习】调试技巧
vLLM 是一款专为大语言模型推理加速而设计的框架,实现了 KV 缓存内存几乎零浪费,解决了内存管理瓶颈问题。 更多 vLLM 中文文档及教程可访问 →https://vllm.hyper.ai/ 调试挂起与崩溃问题 当一个 vLLM 实例挂起或崩溃时,调试问题会非常…...
UML中的用例图和类图
在UML(统一建模语言)中,**用例图(Use Case Diagram)和类图(Class Diagram)**是两种最常用的图表类型,分别用于描述系统的高层功能和静态结构。以下是它们的核心概念、用途及区别&…...
谷粒微服务高级篇学习笔记整理---异步线程池
多线程回顾 多线程实现的4种方式 1. 继承 Thread 类 通过继承 Thread 类并重写 run() 方法实现多线程。 public class MyThread extends Thread {@Overridepublic void run() {System.out.println("线程运行: " + Thread.currentThread().getName());} }// 使用 p…...
清晰易懂的 Flutter 开发环境搭建教程
Flutter 是 Google 推出的跨平台应用开发框架,支持 iOS/Android/Web/桌面应用开发。本教程将手把手教你完成 Windows/macOS/Linux 环境下的 Flutter 安装与配置,从零到运行第一个应用,全程避坑指南! 一、安装 Flutter SDK 1. 下载…...
图形界面设计理念
一、图形界面的组成 1、窗口 窗口约束了图形界面的边界,提供最小化、最大化、关闭的按钮。 2、菜单栏 一般在界面的上方,提供很多功能选项。 3、工具栏 一般是排成一列,每个图标代表一个功能。 工具栏是为了快速的调用经常使用的功能。 4、导…...
MySQL-- 函数(单行函数): 日期和时间函数
目录 1,获取日期、时间 2,日期与时间戳的转换 3,获取月份、星期、星期数、天数等函数 4,日期的操作函数 5,时间和秒钟转换的函数 6,计算日期和时间的函数 7,日期的格式化与解析 1,获取日期、时间 CURDATE() ,CURRENT_DATE() 返回…...
DeepSeek真的超越了OpenAI吗?
DeepSeek 现在确实很有竞争力,但要说它完全超越了 OpenAI 还有点早,两者各有优势。 DeepSeek 的优势 性价比高:DeepSeek 的训练成本低,比如 DeepSeek-V3 的训练成本只有 558 万美元,而 OpenAI 的 GPT-4 训练成本得数亿…...
Node 22.11使用ts-node报错
最近开始学ts,发现使用ts-node直接运行ts代码的时候怎么都不成功,折腾了一番感觉是这个node版本太高还不支持, 于是我找了一个替代品tsx npm install tsx -g npx tsx your-file.ts -g代表全局安装,也可以开发环境安装࿰…...
LabVIEW中VISA Write 与 GPIB Write的差异
在使用 LabVIEW 与 GPIB 设备通讯时,VISA Write Function 和 GPIB Write Function 是两个常用的函数,它们既有区别又有联系。 一、概述 VISA(Virtual Instrument Software Architecture)是一种用于仪器编程的标准 I/O 软件库&…...
牛客练习题——素数(质数)
质数数量 改题目需要注意的是时间 如果进行多次判断就会超时,这时需要使用素数筛结合标志数组进行对所有数据范围内进行判断,而后再结合前缀和将结果存储到数组中,就可以在O(1)的时间复杂度求出素数个数。 #include<iostream>using nam…...
使用MQTTX软件连接阿里云
使用MQTTX软件连接阿里云 MQTTX软件阿里云配置MQTTX软件设置 MQTTX软件 阿里云配置 ESP8266连接阿里云这篇文章里有详细的创建过程,这里就不再重复了,需要的可以点击了解一下。 MQTTX软件设置 打开软件之后,首先点击添加进行创建。 在阿…...
qt实现功率谱和瀑布图
瀑布图 配置qcustomplot的例子网上有很多了,记录下通过qcustomplot实现的功率谱和瀑布图代码: void WaveDisplay::plotWaterfall(MCustomPlot* p_imag) {mCustomPlotLs p_imag;mCustomPlotLs->plotLayout()->clear(); // clear default axis rect…...
通过发音学英语单词:从音到形的学习方法
📌 通过发音学英语单词:从音到形的学习方法 英语是一种 表音语言(phonetic language),但不像拼音文字(如汉语拼音、西班牙语等)那么规则,而是 部分表音部分表意。这意味着我们可以通…...
WebUI问题总结
修改WebUI代码时遇到的一些问题以及解决办法 1. thttpd服务器环境的搭建 可参考《thttpd安装与启动流程》这一篇文章 其中遇到的问题有 thttpd版本问题:版本过旧会导致安装失败,尽量安装新版本thttpd的启动命令失败的话要加上sudo修改文件权限&#…...
23种设计模式-行为型模式-责任链
文章目录 简介问题解决代码核心改进点: 总结 简介 责任链是一种行为设计模式,允许你把请求沿着处理者链进行发送。收到请求后,每个处理者均可对请求进行处理,或将其传递给链上的下个处理者。 问题 假如你正在开发一个订单系统。…...
git commit Message 插件解释说明
- feat - 一项新功能 - fix - 一个错误修复 - docs - 仅文档更改 - style - 不影响代码含义的更改(空白、格式化、缺少分号等) - refactor - 既不修复错误也不添加功能的代码更改 - perf - 提高性能的代码更改 - build - 影响构建系统或外部依赖项…...
推荐系统(二十一):基于MaskNet的商品推荐CTR模型实现
MaskNet 是微博团队 2021 年提出的 CTR 预测模型,相关论文:《MaskNet: Introducing Feature-Wise Multiplication to CTR Ranking Models by Instance-Guided Mask》。MaskNet 通过掩码自注意力机制,在推荐系统中实现了高效且鲁棒的特征交互学习,特别适用于需处理长序列及噪…...
OpenCV 从入门到精通(day_04)
1. 绘制图像轮廓 1.1 什么是轮廓 轮廓是一系列相连的点组成的曲线,代表了物体的基本外形。相对于边缘,轮廓是连续的,边缘不一定连续,如下图所示。其实边缘主要是作为图像的特征使用,比如可以用边缘特征可以区分脸和手…...
多模态学习(八):2022 TPAMI——U2Fusion: A Unified Unsupervised Image Fusion Network
论文链接:https://ieeexplore.ieee.org/stamp/stamp.jsp?tp&arnumber9151265 目录 一.摘要 1.1 摘要翻译 1.2 摘要解析 二.Introduction 2.1 Introduciton翻译 2.2 Introduction 解析 三. related work 3.1 related work翻译 3.2 relate work解析 四…...
JavaEE-0403学习记录
通过前期准备后,项目已经能够成功运行: 1、在文件UserMapper.java中添加如下代码: List<User> selectUSerByIdDynamic(User user); 2、在文件UserMapper.xml中添加如下代码: <select id"selectUSerByIdDynamic&quo…...
图像处理:使用Numpy和OpenCV实现傅里叶和逆傅里叶变换
文章目录 1、什么是傅里叶变换及其基础理论 1.1 傅里叶变换 1.2 基础理论 2. Numpy 实现傅里叶和逆傅里叶变换 2.1 Numpy 实现傅里叶变换 2.2 实现逆傅里叶变换 2.3 高通滤波示例 3. OpenCV 实现傅里叶变换和逆傅里叶变换及低通滤波示例 3.1 OpenCV 实现傅里叶变换 3.2 实现逆傅…...
洛谷题单2-P5715 【深基3.例8】三位数排序-python-流程图重构
题目描述 给出三个整数 a , b , c ( 0 ≤ a , b , c ≤ 100 ) a,b,c(0\le a,b,c \le 100) a,b,c(0≤a,b,c≤100),要求把这三位整数从小到大排序。 输入格式 输入三个整数 a , b , c a,b,c a,b,c,以空格隔开。 输出格式 输出一行,三个整…...
RNN模型与NLP应用——(7/9)机器翻译与Seq2Seq模型
声明: 本文基于哔站博主【Shusenwang】的视频课程【RNN模型及NLP应用】,结合自身的理解所作,旨在帮助大家了解学习NLP自然语言处理基础知识。配合着视频课程学习效果更佳。 材料来源:【Shusenwang】的视频课程【RNN模型及NLP应用…...
