C++ 概览并发

并发

资源管理

资源

  • 程序中符合先获取后释放(显式或隐式)规律的东西,比如内存、锁、套接字、线程句柄和文件句柄等。
  • RAII: (Resource Acquisition Is Initialization),也称为“资源获取就是初始化”,是C++语言的一种管理资源、避免泄漏的惯用法。
unique_ptr & shared_ptr
  • unique_ptr 是一个独立对象或者是数组的句柄,unique_ptr 通过移动操作使得简单高效
  • shared_ptr 很多方面和unique_ptr 相似, 唯一区别是shared_ptr 通过拷贝操作而非移动操作。多个shared_ptr 共享该对象的所有权,只有当最后一个 shared_ptr 被销毁时,对象才被销毁。
  • shared_ptr 提供的垃圾回收机制需要慎重使用。 shared_ptr 使得对象的生命周期变得不那么容易掌控了,除非在一定程度上一定要使用共享所有权,否则别轻易使用shared_ptr
  • shared_ptr 没有指定哪个拥有者有权读写对象
  • 当我们使用函数返回集合的时候,不必使用指针,因为如果定义了移动语义, 在返回的时候会默认使用移动操作

并发

任务 和thread

  • 线程(thread)是任务在程序中的系统级表示。
  • thread 在 中定义
thread 的使用方法
#include <iostream>
#include <thread>
#include <vector>
#include "config.h"// thead 的使用
// 函数的参数这里必须是const, 否则会报thread:120:44: error: static assertion failed: std::thread arguments must be invocable after conversion to rvalues
// 在多线程中使用io 操作,需要考虑线程安全,打印出来的字符服务保证输出内容的顺序
void f(const std::vector<double>& v) {std::cout<< "function f" << std::endl;
}
// 通过res 指针返回thread 的执行结果,不美观的方法
// 不推荐使用此方法,因为无法掌控res 什么时候写入了,一般用消息队列,或者使用condition 、promise等方式来实现返回结果
void f2(const std::vector<double>& v, double* res) {}struct F {std::vector<double> & v;F(std::vector<double>& vv):v{vv} {}/* 如果不使用调用运算符重载, 会编译报错: hread:120:44: error: static assertion failed: std::thread arguments must be invocable after conversion to rvalues*/void operator()() {   // 调用运算符重载std::cout << "struct F  operator" << std::endl;}};class Foo
{
public:void bar(){for (int i = 0; i < 5; ++i){std::cout << "正在执行线程3\n";++n;std::this_thread::sleep_for(std::chrono::milliseconds(10));}}int n = 0;
};int main(int argc, char **argv) {std::vector<double> some_vec {1,2,3,4,5,6};std::vector<double> vec2{10,11,12,13};std::thread t1{f, some_vec}; // 在t1 的线程里面执行函数f, 参数是some_vecstd::thread t2{F{vec2}};	// 在t2 的县城里面执行函数 F 的调用运算符, 参数是vec2double res;std::thread t3(f2, some_vec, &res);Foo f;std::thread t4(&Foo::bar, &f); // t5 在对象 f 上运行 foo::bar()t1.join();t2.join();return 0;
}
  • thread 更多用法清参照cppreference
thread 共享数据
  • mutex // 访问数据排他处理
  • condition_variable 允许一个thread 等待另外一个thread
  • 代码示例
class Message {
private:int msgId;
public:Message(int id) : msgId(id){}int getId() { return msgId;}
};std::queue<Message> mMsgQueue;
std::condition_variable mCond;
std::mutex mMutex;void consumer() {while(true) {//std::cout << "wait mutex " <<  std::endl;std::unique_lock<std::mutex> lck{mMutex};//std::cout << "wait condition " <<  std::endl;//while(mCond.wait(lck))/*do nothing*/;mCond.wait(lck);//std::cout << "after wait condition " <<  std::endl;auto m = mMsgQueue.front();mMsgQueue.pop();std::cout << "get msg Id: " << m.getId() << std::endl;// lck.unlock();}
}void producer() {int index = 10;while(index > 0) {Message msg(index);std::unique_lock<std::mutex> lck{mMutex};mMsgQueue.push(msg);std::cout << "push msg Id: " << msg.getId() << std::endl;lck.unlock(); //(1)mCond.notify_all();//std::cout << "after notify all: " << msg.getId() << std::endl;//std::this_thread::sleep_for(std::chrono::milliseconds(20)); //(2)index--;}
}int main(int argc, char **argv) {std::thread t2(consumer);std::thread t1(producer);t1.join();t2.join();
}
  • 对于生产者-消费者模式,理想状态下,生产者生产一个,消费者就消费一个,但是实际并非如此

    • (1) 如果不主动调用lck.unlock(), lck 需要等到while(){} 进入下一个循环才会释放,所以无论是否加(), cosumer 线程得不到执行的契机(可能和平台有关)
    • (2) 对于加了(1) 而没有(2) 的场合while 很快执行到下一个循环,又立即执行到lck, 也会导致在producer 执行过程中,consumer 得不到执行
      执行效果大致如下:
      在这里插入图片描述
  • 而如果加了(1) 和(2)后:

    在这里插入图片描述

  • 对于consume 如上图的处理方式,获取一次锁只pop 一次数据,会因为producer notify 和consumer 的wait 不是时间上不是匹配出现而导致数据没有被全部读取出来,可以修改为如下方式:

  • 在获取到一次锁后,把消息队列中的消息全部处理掉

void consumer() {while(true) {//std::cout << "wait mutex " <<  std::endl;std::unique_lock<std::mutex> lck{mMutex};//std::cout << "wait condition " <<  std::endl;//while(mCond.wait(lck))/*do nothing*/;mCond.wait(lck);//std::cout << "after wait condition " <<  std::endl;while(!mMsgQueue.empty()) {auto m = mMsgQueue.front();mMsgQueue.pop();std::cout << "get msg Id: " << m.getId() << std::endl;}// lck.unlock();}
}

执行效果大致如下, 这样只是消费者不会立马消费掉生产者生产的数据,但是即使没有(1)(2)也不会导致消息得不到处理:
在这里插入图片描述

任务通信

  • c++ 中主要提供了以下三种,都定义在 头文件中。
    • future and promise 用来从一个独立线程上创建出的任务返回结果
    • packaged_task 是帮助启动义务以及链接返回结果的机制
    • async 以非常类似调用函数的方式启动一个任务
future and promis
  • 允许两个任务间传输直,而无须显示使用锁–“系统”高效地实现了这种传输。
    • 当一个任务需要向另一个任务传输这个值时,把它放入promise。
    • c++ 实现会出现在对应的future 中。
    • 使用future 的get()函数,如果值还未准备好,线程会阻塞直至值准备好。如果get( )无法计算出来,get()会抛一个异常(系统或者从get() 得到数据的任务)
    • promise 的作用主要为future的get() 提供放置 操作(set_value()) 和set_exception().
#include <iostream>
#include <thread>
#include <future>
#include <functional>
void work (std::promise<int>& px) {try {px.set_value(11);} catch (...) {px.set_exception(std::current_exception());}
}void result (std::future<int>& res) {try {int result = res.get();std::cout<< " recieve the result: " << result << std::endl;} catch(...) {//std::cout<<"exception"<< std::current_exception() << std::endl;}
}
int main(int argc, char **argv) {std::promise<int> pro;std::future<int> fut = pro.get_future(); //binding the future and the promisestd::thread t1(work, std::ref(pro));std::thread t2(result, std::ref(fut));t1.join();t2.join();return 0;
}
  • 可以看到使用步骤如下:

    • 声明promise
    • 定义future,并且绑定promise 和future, 二者的模板类型必须一致(很好理解,provise 设定的就是传给future 的)
    • 使用promise 的set_value()或者set_exception() 传递值
    • 使用future 的get() 函数接收值(注意get()是阻塞的)
  • 这里使用了std::ref std::ref的使用场景:

    • 算法传递可调用对象,而这些对象可能需要引用传递。
    • 在多线程之间共享数据,但有希望通过引用传递数据而不是复制。
    • std::ref可以使函数接受任何类型的引用而不仅仅是特定类型
packaged_task
  • packaged_task 简化了任务连接future & promise.
  • (TODO)

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

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

相关文章

[运维|系统] linux修改了/etc/fstab文件后,检查/etc/fstab文件是否正常

要求检查的原因 当修改了 /etc/fstab文件后&#xff0c;如果fstab中的内容出现错误&#xff0c;则会导致下次开机时&#xff0c;系统无法进入系统 检查命令 使用以下命令检查 fstab 文件内容是否正常 mount -a这个命令会尝试挂载 fstab 文件中列出的所有文件系统。如果 fst…

Vue.js-----vue组件

能够说出vue生命周期能够掌握axios的使用能够了解$refs, $nextTick作用能够完成购物车案例 Vue 生命周期讲解 1.钩子函数 目标&#xff1a;Vue 框架内置函数&#xff0c;随着组件的生命周期阶段&#xff0c;自动执行 作用: 特定的时间点&#xff0c;执行特定的操作场景: 组…

hw蓝初中级面试题整理(流量特征+场景题)

Webshell检测&#xff1a; 0、d盾河马阿里伏魔查杀1、有具体时间的话可以根据时间和正则匹配关键字定位&#xff0c;然后封ip&#xff0c;追踪ip最后做了什么2、在网络层监控 HTTP&#xff0c;并且检测到有人访问了一个从没访问过的文件&#xff0c;而且返回了 200&#xff0c…

SpringBoot使用注解标注过滤器:跟踪请求日志、处理跨域问题

文章目录 引言I 跟踪请求日志(LogFilter)II 过滤器处理跨域问题III 拦截器和过滤器的原理3.1 Filter的执行顺序在Interceptor之前3.2 RequestInterceptor将MDC中request_id传入header引言 @WebFilter将一个实现了javax.servlet.Filter接口的类定义为过滤器 属性filterName声明…

[前后端基础]图片详解

[前后端基础]图片传输与异步-CSDN博客 https://juejin.cn/post/6844903782959022093#heading-3 base64、file和blob用JS进行互转的方法大全【前端】_js base64转blob-CSDN博客 后端存储方式 对于第一种存储方式&#xff0c;我们前端直接将存储路径赋值给 src 属性即可轻松显示。…

BufferGeometry--持续更新

.center&#xff08;&#xff09; 在Three.js中&#xff0c;BufferGeometry 对象的 .center() 方法是用于将几何体的原点&#xff08;0, 0, 0&#xff09;移到其包围盒的中心。这通常在你想要旋转、缩放或者对齐几何体时非常有用&#xff0c;因为它确保了这些操作是相对于几何…

STM32——GPIO输出(点亮第一个LED灯)

代码示例&#xff1a; #include "stm32f10x.h" // Device headerint main() {RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//开启时钟GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode GPIO_Mode_Out_PP;GPIO_InitSt…

租赁商城小程序源码部署/售后更新/搭建上线维护

基于ThinkPHPFastAdminUniApp开发的租赁商城小程序&#xff0c;提供用户物品租赁服务的应用程序&#xff0c;方便客户搭建各种类型的租赁场景服务。通过小程序端多角色进行平台管理&#xff0c;用户租赁商品缴纳租金及押金&#xff0c;员工端可操作商品出库和归还&#xff0c;订…

文件夹名批量重命名:一键实现中文到意大利语的高效翻译!文件管理更高效!

文件夹管理成为了我们日常工作和生活中的重要一环。当文件夹名称繁杂且多样&#xff0c;尤其是涉及到不同语言时&#xff0c;如何快速、准确地批量重命名&#xff0c;成为了许多人的迫切需求。今天&#xff0c;我们就来聊聊如何轻松实现文件夹名从中文到意大利语的批量翻译&…

vue2中的配置代理和路由的使用

1.配置代理 在Vue.js 2中配置代理主要是为了解决开发环境下的跨域问题&#xff0c;通常用于前后端分离开发中。Vue.js本身并不提供代理功能&#xff0c;但是可以通过webpack-dev-server或者Vue CLI提供的配置进行代理设置。 下面是在Vue.js 2中配置代理的一般步骤&#xff1a…

用awk做log聚合

123456valueindexvaluevalueindex 日前拿到一份log&#xff0c;一个完整数据集被写在两条log里&#xff0c;通过索引字段排序后&#xff0c;呈现出如上表样式的csv格式。为了方便统计&#xff0c;利用awk对数据进行了聚合&#xff0c;把同一索引值的log两两合并在一起&#xf…

基于FPGA的视频矩阵切换方案

一、单个显示设备的系统方案&#xff1a;会议室只有1个显示设备 会议室的信号源有很多&#xff0c;但是显示设备只有1个&#xff0c;这个时候最佳方案是使用切换器。 &#xff08;1&#xff09;切换器&#xff08;控制方式&#xff1a;遥控器、软件、机箱面板、中控&#xff…

版本控制工具之Git的基础使用教程

Git Git是一个分布式版本控制系统&#xff0c;由Linux之父Linus Torvalds 开发。它既可以用来管理和追踪计算机文件的变化&#xff0c;也是开发者协作编写代码的工具。 本文将介绍 Git 的基础原理、用法、操作等内容。 一、基础概念 1.1 版本控制系统 版本控制系统&#x…

Jmeter压测问题汇总

缘起 公司的Elasticsearch流量日益减少&#xff0c;经过评估&#xff0c;我们计划迁移到成本更低的Mysql集群上&#xff0c;需要对接口进行压力复测 版本 JDK17&#xff08;会有兼容性问题&#xff0c;建议使用JDK17之前的版本&#xff09;Jmeter 5.2.1 windows系统下临时端…

Python-----容器的介绍以及操作

1.列表和元组 1.列表是什么, 元组是什么&#xff1a; 编程中, 经常需要使用变量, 来保存/表示数据. 如果代码中需要表示的数据个数比较少, 我们直接创建多个变量即可. 但是有的时候, 代码中需要表示的数据特别多, 甚至也不知道要表示多少个数据. 这个时候, 就需要用到列表 列表…

论文解读--High-resolution Automotive Radar Point Cloud Imaging and Processing

高分辨汽车雷达点云成像和处理 摘要 汽车雷达具有体积小、硬件成本低、全天候工作、高分辨率等公认的优点&#xff0c;是高级驾驶辅助系统(ADAS)必不可少的一类重要传感器。然而&#xff0c;低角度分辨率和低成像性能的限制很难满足下一阶段ADAS的需要。新兴的4D成像雷达采用多…

C语言数据结构与算法之栈练习2:判断栈是否为空或者是否已满

练习2&#xff1a;判断栈是否为空或者是否已满 需求1&#xff1a;实现一个简单的栈结构&#xff0c;并实现入栈和出栈的功能&#xff0c;编写相关的代码进行测试。 需求2&#xff1a;如果栈已满&#xff0c;则不要再继续入栈&#xff0c;并提示错误信息。如果栈为空&#xff…

计算机视觉与深度学习实战之以Python为工具:基于融合特征的以图搜图技术

注意:本文的下载教程,与以下文章的思路有相同点,也有不同点,最终目标只是让读者从多维度去熟练掌握本知识点。 下载教程:计算机视觉与深度学习实战-以MATLAB和Python为工具_基于融合特征的以图搜图技术_项目开发案例教程.pdf 一、引言 随着信息技术的飞速发展,图像数据呈…

常量池和运行时常量池以及字符串常量池的区别

1. 常量池 常量池是Java编译器生成的&#xff0c;存储在.class文件中的数据集合&#xff0c;它包含了类中所有的字面量和符号引用。 字面量如字符串、整数、浮点数等&#xff1b;符号引用如类和接口的全名、字段的名称和数据类型、方法的名称和描述符等。常量池的目的是为了减…

SSC369G 双4K高性价比AI IPC方案

一、方案描述 SSC369G 双4K高性价比AI IPC方案采用主芯片SSC369G&#xff0c;内核为CA55四核最高主频为1.5Ghz处理器。SOC内置集成一个64位的四核RISC处理器&#xff0c;先进的图像信号处理器&#xff08;ISP&#xff09;&#xff0c;高性能的H.265/H.264/MJPEG视频编解码器&a…