C++并发编程

news/2024/5/19 1:51:23

基本介绍

线程

  • 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.mrgr.cn/p/81660285

相关文章

多线程中frida定位关键线程的方法

pthread_create 会先得到一个pthread_internal_t结构体最后会调用__pthread_internal_add并将返回值赋给pthread_create的第一个参数thread_out__pthread_internal_add 会将传入的 pthread_internal_t 加入到g_thread_list全局线程列表中,然后将传入的pthread_internal_t返回,…

【Qt 专栏】QByteArray详解(QByteArray 与 QString的区别)

本文转自:《Qt编程指南》 作者:奇先生 Qt编程指南,Qt新手教程,Qt Programming Guide 本节学习 QByteArray 的两种用法,第一种作为字符串处理类,类似 QString ,但 QByteArray 内部字符编码不确定,所以要慎用。第二种是作为纯的字节数组,里面可以包含多个 \0 ,经…

前端调用DRI后端API出现跨域资源共享(CORS)问题解决办法

在进行后端API开发时,有时会遇到“跨域资源共享 (CORS) 请求...被阻止“的错误,如图1所示。本文讲解如何在使用DRF(Django REST Framework)的后端API开发项目中解决这个问题。目录1. 引言2. 跨源资源共享和实现方法3. 在Django项目中配置django-cors-headers库Reference 1.…

CPDA|0到1突破:构建高效数据分析体系的秘密武器

在现今信息爆炸的时代&#xff0c;数据已经渗透到了我们生活的方方面面&#xff0c;成为了决策、创新和竞争优势的关键。因此&#xff0c;构建一套高效的数据分析体系&#xff0c;对于企业和个人而言&#xff0c;都是至关重要的。那么&#xff0c;如何在众多的数据海洋中脱颖而…

【超简单实用】Zotero 7 内置pdf背景颜色更改插推荐以及安装

Zotero beta7 pdf 内置颜色更换 zetore 6 很多成熟的插件在 zetore 7都不能用了。版本回退看起来内置文章的注释会被消除&#xff0c;所以又不想退回去。前几个月在找beta 7 的pdf 护眼色的插件一直没有&#xff0c;今天终于发现了&#xff01;&#xff01;&#xff01;&#…

数据结构——单向循环链表

一、单向循环链表 (一)单向循环链表的构造 单向循环链表的尾结点的指针域中必须指向链表的首结点的地址) 1)构造单向循环链表的结点 //单向循环链表中的结点有效数据类型,用户可以根据需要进行修改 typedef int DataType_t;//构造单向循环链表的结点,链表中所有结点的数据类型…

k8s网络配置

1 基础概念 1.1 containerPort、port、nodePort、targetPort的区别与联系containerPort:Container容器暴露的端口。containerPort是在pod控制器中定义的、pod中的容器需要暴露的端口。 port:service暴露在集群中的端口,仅限集群内部访问。port是暴露在cluster (集群网络)上的…

TCP-模拟BS架构通信

简介 bs是通过浏览器进行访问的每次访问都会开启一个短期的socket用来访问服务器的资源 响应报文的格式 服务端 bs架构中的b是浏览器&#xff0c;不需要我们书写&#xff0c;我们只需要书写服务端即可 服务端 public class Server {public static void main(String[] args) {S…

pg数据库id是vachar类型,需要按照数字大小排序

pg数据库id是vachar类型,需要按照数字大小排序 select * from table order by id; 直接拿order by id条件排序,会出现1002排在10019后面应该将id转化成int类型再排序,修改结果如下: select * from table order by cast(id,integer);本文来自博客园,作者:lsblk0402,转载请…

Poco框架实操:获取节点属性的高效技巧(一)

上周我们分享了Poco节点的各种关系以及使用场景,那么本周我们来聊一下,如何获取关于节点的更多信息。此文章来源于项目官方公众号:“AirtestProject” 版权声明:允许转载,但转载必须保留原链接;请勿用作商业或者非法用途一、前言 上期推文我们介绍了Poco UI树下的节点关系…

高端制造业的经销商文件分发,怎样才能降低管理成本和风险?

对于高端制造业来说,经销商在制造业供应链中扮演着重要的角色,作为制造商与零售商之间的中介,协助制造商将其产品打入市场,扩大产品销售范围。因此制造业生产商与经销商之间存在紧密且频繁的文件传输需求,一个制造业生产商可能要与上百个经销商存在业务往来,经销商文件分…

【炼金术士】BatchSize对网络训练的影响

文章目录 1 BatchSize对于网络训练的影响2 调整学习率可以提高大BatchSize的性能3 实际训练时的建议3.1 设置初始学习率的方法3.2 多卡训练时学习率的设置 参考资料&#xff1a; 【深度学习】Batch Size对神经网络训练的影响【AI不惑境】学习率和batchsize如何影响模型的性能&…

报告!这里发现了一个赛博炼丹的神级平台!

众所周知,“赛博炼丹”是一个AI开发研究领域古老又神秘的活动,它往往对炼丹平台有很高的要求。如果你也是一路从“炼丹小白”成长到“资深AI算法工程师”,那你一定懂我在说什么?说好了,天台见!众所周知,“赛博炼丹”是一个AI开发研究领域古老又神秘的活动,它往往对炼丹…

2024年汽车软件开发状况调查结果出炉:软件研发人员必看

在今年1月底,嵌入式静态分析领域公认的行业领导及先驱Perforce公司联合北汇信息首次诚挚邀请中国汽车软件开发专业人士参加2024年汽车发展行业状况报告的调查。现调查结果报告已新鲜出炉!在全球调查结果中,本次调查来自亚洲的答卷占比39%,其中中国在亚洲内占比46%。如您希望…

大模型必备 - 中文最佳向量模型 acge_text_embedding

近期,上海合合信息科技股份有限公司发布的文本向量化模型 acge_text_embedding 在中文文本向量化领域取得了重大突破,荣获 Massive Text Embedding Benchmark (MTEB) 中文榜单(C-MTEB)第一名的成绩。这一成就标志着该模型将在大模型领域的应用中发挥更加迅速和广泛的影响。…

高端制造企业生产设备文件管理,怎样保证好用不丢失文件?

高端制造业在市场经济中占据重要角色,在高端制造业企业内部,生产设备又是最关键的一环环,它们不仅负责完成生产任务,同时也会产生大量的文件。这些数据反映了设备的运行状态、生产效率、能源消耗以及产品质量等多个方面,因此做好生产设备文件管理至关重要。 设备运行数据…

使用 Meta Llama 3 构建人工智能的未来

使用 Meta Llama 3 构建人工智能的未来 现在提供 8B 和 70B 预训练和指令调整版本,以支持广泛的应用 使用 Meta AI 体验 Llama 3 我们已将 Llama 3 集成到我们的智能助手 Meta AI 中,它扩展了人们完成工作、创造和与 Meta AI 联系的方式。通过使用 Meta AI 进行编码任务和解…

【后端】PyCharm的安装指引与基础配置

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、PyCharm是什么二、PyCharm安装指引安装PyCharm社区版安装PyCharm专业版 三、配置PyCharm&#xff1a;四、总结 前言 随着开发语言及人工智能工具的普及&am…

可视化智慧工厂

在科技迅猛发展的今天&#xff0c;制造业正迎来一场深刻的变革——智慧工厂的崛起。可视化智慧工厂作为其中的重要一环&#xff0c;以其直观、高效、智能的特点&#xff0c;正成为制造业转型升级的关键所在。 一、什么是可视化智慧工厂? 传统的制造业生产方式往往依赖于人工…

机器学习/算法工程师面试题目与答案-数学基础部分

机器学习/算法工程师面试题目--数学基础部分 一、数学基础1、微积分SGD,Momentum,Adagard,Adam原理L1不可导的时候该怎么办sigmoid函数特性 2、统计学&#xff0c;概率论求 Max(a, b) 期望拿更长的玫瑰花的最好策略最大化工作天数的员工数切比雪夫不等式随机截成三段组成三角形…