C++并发编程

基本介绍

线程

  • C++98标准没有直接提供原生的多线程支持

  • 在C++98中,并没有像后来的C++11标准中那样的<thread>库或其他直接的多线程工具

  • 然而,这并不意味着在C++98中无法实现多线程。开发者通常会使用平台特定的API(如Windows的线程API或POSIX线程(pthreads))来实现多线程。此外,一些第三方库,如Boost.Thread,也提供了在C++98环境中进行多线程编程的功能

  • 需要注意的是,使用平台特定的API或第三方库进行多线程编程可能会增加代码的复杂性和维护成本,因为你需要确保你的代码在所有目标平台上都能正常工作,并处理各种可能的线程同步和互斥问题

  • 从C++11开始,C++标准库开始直接支持多线程编程,通过引入<thread><mutex><condition_variable>等头文件,提供了更为方便和统一的多线程编程接口。因此,如果可能的话,建议使用C++11或更高版本的C++标准进行多线程编程

进程

  • C++标准库本身并没有直接提供创建进程的功能,创建进程通常依赖于操作系统的API或其他库函数

  • 在Unix和Linux系统中,可以使用fork()函数来创建进程。fork()函数会创建一个与当前进程几乎完全相同的子进程,包括代码、数据和堆栈等。在子进程中,可以使用exec()系列函数来执行另一个程序

  • 在Windows系统中,可以使用CreateProcess()函数来创建进程。这个函数会创建一个新的进程,并返回一个进程句柄,可以用于操作该进程

创建线程

#include<iostream>
#include<string>
#include<thread>void printHelloWorld()
{std::cout << "Hello World" << std::endl;
}void print(std::string text)
{std::cout << text << std::endl;
}int main()
{// 当前 main 这里是主线程// 创建一个线程 t1,让它执行 printHelloWorld 这个函数std::thread t1(printHelloWorld);// 等待 t1 线程完成(如果不等待,可能子线程 t1 还没完成的时候,主线程已经结束了,程序会报错)t1.join();// 创建一个线程 t2,让它执行 print 这个函数,并传入参数std::thread t2(print, "This is thread 2.");// 等待 t2 线程完成(如果不等待,可能子线程 t2 还没完成的时候,主线程已经结束了,程序会报错)t2.join();// 创建一个线程 t3std::thread t3(print, "This is thread 3.");// 分离线程(也可以使用分离线程这个技术,让主线程结束后,子线程依然可以运行)t3.detach();// 创建一个线程 t4std::thread t3(print, "This is thread 3.");// 严谨的项目里面,可能用到,先判断该线程是否可以被join()bool isJoin = t3.joinable();if (isJoin){t3.join();}return 0;
}

线程常见错误

  1. 传递临时变量的问题
#include<iostream>
#include<thread>void foo(int& x)
{x += 1;
}int main()
{int num = 1;		// 局部变量 numstd::thread t1(foo, std::ref(num));		// std::ref() 传递引用类型t1.join();		// 等待t1线程结束std::cout << num << std::endl;return 0;
}
  1. 传递指针或引用指向局部变量的问题
#include<iostream>
#include<thread>// 创建一个线程 t (全局变量)
std::thread t;
int a = 1;void foo(int& x)
{std::cout << x << std::endl;		// 1x += 1;std::cout << x << std::endl;		// 2
}void test()
{t = std::thread(foo, std::ref(a));
}int main()
{test();t.join();return 0;
}
  1. 入口函数为类的私有成员函数
#include<iostream>
#include<thread>
#include<memory>	// 智能指针,不用的时候,会自动释放class A
{
private:friend void thread_foo();void foo(){std::cout << "hello" << std::endl;}
};void thread_foo()
{std::shared_ptr<A> a = std::make_shared<A>();	// 使用智能指针实例化A对象std::thread t(&A::foo, a);t.join();
}int main()
{thread_foo();
}

互斥量

锁的使用

#include<iostream>
#include<thread>
#include<mutex>int a = 0;// 创建互斥锁
std::mutex mtx;void func()
{for (int i = 0; i < 10000; i++){mtx.lock();		// 加锁a += 1;mtx.unlock();	// 解锁}
}int main()
{std::thread t1(func);std::thread t2(func);t1.join();t2.join();std::cout << a << std::endl;return 0;
}

死锁演示

  • 图形演示

在这里插入图片描述

  • 代码演示
#include<iostream>
#include<thread>
#include<mutex>// 创建互斥锁
std::mutex mtx1;
std::mutex mtx2;// people1 先抢占 mtx1,再快速抢占 mtx2
void people1()
{for (int i = 0; i < 1000; i++){mtx1.lock();std::cout << "people1 上锁mtx1成功\n";mtx2.lock();std::cout << "people1 上锁mtx2成功\n";mtx1.unlock();mtx2.unlock();}
}// people2 先抢占 mtx2,再快速抢占 mtx1
void people2()
{for (int i = 0; i < 1000; i++){mtx2.lock();std::cout << "people2 上锁mtx2成功\n";mtx1.lock();std::cout << "people2 上锁mtx1成功\n";mtx1.unlock();mtx2.unlock();}
}int main()
{std::thread t1(people1);std::thread t2(people2);t1.join();t2.join();return 0;
}

解决死锁

  • 解决办法:所有人都要严格按照顺序抢占资源,都要先抢占完A资源,才能继续抢占B资源,继续抢占C资源…
#include<iostream>
#include<thread>
#include<mutex>// 创建互斥锁
std::mutex mtx1;
std::mutex mtx2;// people1 要先抢占 mtx1,才能抢占 mtx2
void people1()
{for (int i = 0; i < 1000; i++){mtx1.lock();std::cout << "people1 上锁mtx1成功\n";mtx2.lock();std::cout << "people1 上锁mtx2成功\n";mtx1.unlock();std::cout << "people1 解锁mtx1成功\n";mtx2.unlock();std::cout << "people1 解锁mtx2成功\n";}
}// people2 要先抢占 mtx1,才能抢占 mtx2
void people2()
{for (int i = 0; i < 1000; i++){mtx1.lock();std::cout << "people2 上锁mtx1成功\n";mtx2.lock();std::cout << "people2 上锁mtx2成功\n";mtx1.unlock();std::cout << "people2 解锁mtx1成功\n";mtx2.unlock();std::cout << "people2 解锁mtx2成功\n";}
}int main()
{std::thread t1(people1);std::thread t2(people2);t1.join();t2.join();return 0;
}

lock_guard

  • std::lock_guard 是 C++ 标准库中的一种互斥量封装类,用于保护共享数据,防止多个线程同时访问同一资源而导致的数据竞争问题
  • 当构造函数被调用时,该互斥量会被自动锁定
  • 当析构函数被调用时,该互斥量会被自动解锁
  • std::lock_guard 对象不能复制或移动,因此它只能在局部作用域中使用
#include<iostream>
#include<thread>
#include<mutex>// 共享变量
int shared_data = 0;// 创建互斥锁
std::mutex mtx;void func()
{for (int i = 0; i < 1000; i++){std::lock_guard<std::mutex> obj(mtx);	// 构造时自动加锁,析构时自动解锁shared_data++;}
}int main()
{std::thread t1(func);std::thread t2(func);t1.join();t2.join();std::cout << shared_data << std::endl;		// 2000return 0;
}

unique_lock

  • std::unique_lock 是 C++ 标准库中提供的一个互斥量封装类,用于在多线程程序中对互斥量进行加锁和解锁操作
  • 它的主要特点是可以对互斥量进行更加灵活的管理,包括延迟加锁、条件变量、超时等

std::unique_lock 提供了以下几个成员函数

lock():尝试对互斥量进行加锁操作,如果当前互斥量已经被其他线程持有,则当前线程会被阻塞,直到互斥量被成功加锁。try_lock():尝试对互斥量进行加锁操作,如果当前互斥量已经被其他线程持有,则函数立即返回 false,否则返回 truetry_lock_for(const std::chrono::duration<Rep, Period>& rel_time):尝试对互斥量进行加锁操作,如果当前互斥量已经被其他线程持有,则当前线程会被阻塞,直到互斥量被成功加锁,或者超过了指定的时间try_lock_until(const std::chrono::time_point<Clock, Duration>& abs_time):尝试对互斥量进行加锁操作,如果当前互斥量已经被其他线程持有,则当前线程会被阻塞,直到互斥量被成功加锁,或者超过了指定的时间点unlock():对互斥量进行解锁操作

std::unique_lock 还提供了以下几个构造函数

unique_lock() noexcept = default:默认构造函数,创建一个未关联任何互斥量的 std::unique_lock 对象explicit unique_lock(mutex_type& m):构造函数,使用给定的互斥量 m 进行初始化,并对该互斥量进行加锁操作unique_lock(mutex_type& m, defer_lock_t) noexcept:构造函数,使用给定的互斥量 m 进行初始化,但不对该互斥量进行加锁操作unique_lock(mutex_type& m, try_to_lock_t) noexcept:构造函数,使用给定的互斥量 m 进行初始化,并尝试对该互斥量进行加锁操作。如果加锁失败,则创建的 std::unique_lock 对象不与任何互斥量关联unique_lock(mutex_type& m, adopt_lock_t) noexcept:构造函数,使用给定的互斥量 m 进行初始化,并假设该互斥量已经被当前线程成功加锁

文章推荐

  • std::thread 快速上手:https://blog.csdn.net/m0_75215937/article/details/135007590
  • std::thread 更多内容:https://blog.csdn.net/sjc_0910/article/details/118861539
  • c++ 多线程编程:https://zhuanlan.zhihu.com/p/547312117

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

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

相关文章

【C/C++】动态内存管理(C:malloc,realloc,calloc,free || C++:new,delete)

&#x1f525;个人主页&#xff1a; Forcible Bug Maker &#x1f525;专栏&#xff1a; C | | C语言 目录 前言C/C内存分布C语言中的动态内存管理&#xff1a;malloc/realloc/realloc/freemallocrealloccallocfree C中的动态内存管理&#xff1a;new/deletenew和delete操作内…

微信小程序:9.小程序配置

全局配置文件 小程序根目录下的app.json文件是小程序的全局配置文件。 常用的配置文件如下: pages 记录当前小程序所有的页面存放路径信息 window 全局设置小程序窗口外观 tabBar 设置小程序底部的tabBar效果 style 是否启用新版style 小程序窗口的组成部分 了解windo节点常…

HTTP:强缓存优化实践

强缓存&#xff1a;浏览器不会向服务器发送任何请求&#xff0c;直接从本地缓存中读取文件 强缓存是指浏览器在向服务器请求资源时&#xff0c;判断本地是否存在该资源的缓存&#xff0c;并判断是否过期。 如果本地缓存未过期&#xff0c;浏览器就直接使用本地缓存&#xff0c…

element 分页切换时:current-page无效 页数不会跟着一起切换

问题回溯&#xff1a;使用el-pagination组件 选择切换当前分页 页数为2 问题结果&#xff1a;el-pagination组件 当前页切换失败 一直都是 1&#xff0c;接口传参分页数据是2&#xff0c;打印当前分页也是2 解决方案1&#xff1a;使用 current-page参数 .sync 修饰符 解决方案2…

微信小程序简单实现购物车功能

微信小程序简单实现购物车结算和购物车列表展示功能 实现在微信小程序中对每一个购物车界面的商品订单&#xff0c;进行勾选结算和取消结算的功能&#xff0c;相关界面截图如下&#xff1a; 具体实现示例代码为&#xff1a; 1、js代码&#xff1a; Page({/*** 页面的初始数…

清理电脑垃圾软件

清理电脑垃圾软件 1.geek.exe 强力卸载 2. 磁盘分析 直接删除 log 磁盘文件大小分析工具WizTree_v4.01

TCP相关问题总结

文章目录 TCP连接建立过程1. TCP三次握手2. TCP四次挥手3. TCP为什么是三次握手4. TCP为什么是四次挥手 TCP流量控制TCP拥塞控制1. 为什么需要拥塞控制2. 控制手段 TCP连接建立过程中出现丢包 TCP连接建立过程 1. TCP三次握手 首先client端发出连接请求&#xff0c;并且请求同…

paddle ocr模型量化实践

参考&#xff1a;https://github.com/PaddlePaddle/PaddleOCR/blob/main/deploy/slim/quantization/README.md https://github.com/PaddlePaddle/PaddleOCR/blob/release/2.7.1/doc/doc_ch/FAQ.md 蒸馏 剪枝 量化 参考&#xff1a;https://blog.csdn.net/mddCSDN/article/de…

高扬程水泵,提升水源新选择!— 恒峰智慧科技

在炎炎夏日&#xff0c;阳光炙烤着大地&#xff0c;森林火灾的发生频率也随之上升。火势猛烈&#xff0c;烟雾弥漫&#xff0c;给森林带来了极大的破坏。为了保护森林资源&#xff0c;我们必须采取有效的措施来扑灭火灾。而在这其中&#xff0c;高扬程水泵成为了提升水源新选择…

一种基于YOLOv8改进的高精度红外小目标检测算法 (原创自研)

&#x1f4a1;&#x1f4a1;&#x1f4a1;本文摘要&#xff1a;一种基于YOLOv8改进的高精度小目标检测算法&#xff0c; 在红外小目标检测任务中实现暴力涨点&#xff1b; &#x1f4a1;&#x1f4a1;&#x1f4a1;创新点&#xff1a; 1&#xff09;SPD-Conv特别是在处理低分…

手持LED弹幕,超炫特效,让你的每一次出场都耀眼夺目!

在这个快节奏的数字时代&#xff0c;沟通不再局限于言语和文字&#xff0c;就连表白、追星、晚会互动&#xff0c;甚至日常的提词都需要一点科技的火花来点燃气氛。于是&#xff0c;手持LED弹幕滚动屏&#xff0c;这个集实用与趣味于一身的神器&#xff0c;悄然成为了社交场上的…

c++图论基础(2)

目录 图的存储方式&#xff1a; 邻接矩阵&#xff1a; 代码实现&#xff1a; 邻接表&#xff1a; 代码实现&#xff1a; 邻接矩阵邻接表对比&#xff1a; 带权图&#xff1a; 邻接矩阵存储&#xff1a; 邻接表存储(代码实现)&#xff1a; 图的存储方式&#xff1a; 邻…

Python数组类+AI插件

目录 规划实现初始化插入删除查找 AI插件单测注释调优建议 小结 规划 先想清楚都写哪些&#xff0c;然后再动手操作 用Python写了一个简单数组类&#xff0c;首先思考下都写哪些功能&#xff1a; 插入删除查找用插件做单元测试和写注释 目的只是实现一个简单的数组类&#x…

android studio 编译一直显示Download maven-metadata.xml

今天打开之前的项目的时候遇到这个问题:android studio 编译一直显示Download maven-metadata.xml, AI 查询 报错问题&#xff1a;"android studio 编译一直显示Download maven-metadata.xml" 解释&#xff1a; 这个错误通常表示Android Studio在尝试从Maven仓库…

stm32单片机开发一、中断之外部中断实验

stm32单片机的外部中断和定时器中断、ADC中断等都由stm32的内核中的NVIC模块控制&#xff0c;stm32的中断有很多中&#xff0c;比如供电不足中断&#xff0c;当供电不足时&#xff0c;会产生的一种中断&#xff0c;这么多中断如果都接在CPU上&#xff0c;或者说CPU去处理&#…

Druid高性能数据库连接池?SpringBoot整合MyBatis整合SpringMVC整合Druid

文章目录 Druid高性能数据库连接池&#xff1f;SpringBoot整合MyBatis整合SpringMVC整合Druid异常记录spring-boot-starter-parent作用Druid介绍什么是数据库连接池&#xff1f;为什么选择Druid数据库连接池整合SpringBoot,MyBatis,SpringMVC,Druid到Maven项目的真个流程pom文件…

Django-admin组件

Django-admin组件 admin是django中提供的一套可视化工具&#xff1a;用于对ORM中定义的表进行增删改查。 1 概览 在django项目启动时&#xff0c;自动找到注册到admin中的所有model中定义的类&#xff0c;然后为这些类生成一系列的URL和视图函数&#xff0c;实现基本增删改查…

Kafka 3.x.x 入门到精通(06)——Kafka进阶

Kafka 3.x.x 入门到精通&#xff08;06&#xff09;&#x1f449;&#x1f449;&#x1f449;&#x1f449; Kafka进阶 3. Kafka进阶3.1 Controller选举3.2 Broker上线下线3.3 数据偏移量定位3.4 Topic删除3.5 日志清理和压缩3.7 页缓存3.8 零拷贝3.9 顺写日志3.10 Linux集群部…

串口通信标准RS232 RS485 RS422的区别

很多工程师经常把RS-232、RS-422、RS-485称为通讯协议&#xff0c;其实这是不对的&#xff0c;它们仅仅是关于串口通讯的一个机械和电气接口标准&#xff08;顶多是网络协议中的物理层&#xff09;&#xff0c;不是通讯协议&#xff0c;那它们又有哪些区别呢&#xff1a; 第一…

python学习笔记----数据容器(六)

一、数据容器的入门 python中的数据容器&#xff1a;一种可以容纳多份数据的数据类型&#xff0c;容纳的每一份数据称之为1个元素。每一个元素&#xff0c;可以是任意类型的数据&#xff0c;如字符串、数字、布尔等。 数据容器根据特点的不同&#xff0c;如&#xff1a; 是否…