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();



	// 创建一个线程 t3
	std::thread t3(print, "This is thread 3.");

	// 分离线程(也可以使用分离线程这个技术,让主线程结束后,子线程依然可以运行)
	t3.detach();



	// 创建一个线程 t4
	std::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;		// 局部变量 num
	std::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;		// 1
	x += 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;		// 2000

	return 0;
}

unique_lock

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

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

lock():尝试对互斥量进行加锁操作,如果当前互斥量已经被其他线程持有,则当前线程会被阻塞,直到互斥量被成功加锁。

try_lock():尝试对互斥量进行加锁操作,如果当前互斥量已经被其他线程持有,则函数立即返回 false,否则返回 true

try_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.mfbz.cn/a/572961.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

前端开发攻略---用原生JS在网页中也能实现 文本转语音!

1、原理 语音合成 (也被称作是文本转为语音&#xff0c;英语简写是 tts) 包括接收 app 中需要语音合成的文本&#xff0c;再在设备麦克风播放出来这两个过程。 Web API中对此有一个主要控制接口 SpeechSynthesis&#xff0c;外加一些处理如何表示要被合成的文本 (也被称为 utte…

玩转微服务-SonarQube

这里写目录标题 第一节 SonarQube1.1 简介1.2 四个组成部分1.2.1 SonarQube服务器1.2.2 SonarQube数据库1.2.3 插件1.2.4 Scanner 1.3 工作流程 第二节 SonarQube的安装2.1 安装2.2 插件 第三节 P3C规范3.1 简介3.2 SonarQube 配置 P3C规范3.3 IDEA配置 P3C规范 第四节 Maven项…

机器学习-期末复习

本文的内容按照作者的课程考试要求书写&#xff0c;仅供复习参考。&#x1f337;&#x1f337;&#x1f337;欢迎大家指正&#xff01; 机器学习是一种人工智能&#xff08;AI&#xff09;的分支领域&#xff0c;它致力于开发能够通过数据学习和改进的算法和模型。简而言之&…

2024年AIGC+教育行业报告

在宏观层面上&#xff0c;如果把人工智能看作一种生命体&#xff0c;AIGC教育的内涵其实是碳基生命和硅基生命的 交互和培育问题。AIGC技术是对人脑计算、思考、判断等内在能力的延伸&#xff0c;是人的智能在机器形态 上的规模化聚集、运作和反应。由此&#xff0c;部分基础性…

【漏洞复现】云时空社会化商业ERP系统LoginName SQL注入漏洞

漏洞描述&#xff1a; 云时空社会化商业ERP系统loginName存在SQL注入漏洞&#xff0c;攻击者可以通过此漏洞获取数据库敏感信息。 搜索语法: Fofa-Query: app"云时空社会化商业ERP系统" 漏洞详情&#xff1a; 1.云时空社会化商业ERP系统。 2.漏洞POC&#xff1a…

倒计时开始!Big Demo Day第十二期,揭秘DePIN,探索Web3未来(附参会指南)

香港—— 全球领先的 Web3.0 活动 Big Demo Day 第十二期即将于 4 月 26 日在香港数码港盛大举行。本次活动由知名科技企业 ZeeprLabs 赞助&#xff0c;Central Research 主办&#xff0c;并得到 Techub News 的联合主办以及数码港、852Web3 等机构的合作支持。 在过去的 11 期…

鸿蒙HarmonyOS应用 - ArkUI组件

ArkUI组件 基础组件 Image 声明Image组件并设置图片源 网络权限&#xff1a;ohos.permission.INTERNET Image(scr: string | PixelMap | Resource)// 1. string&#xff1a;用于加载网络图片&#xff0c;需要申请网络权限 Image("https://xxx.png")// 2. PixelMap…

驱鸟器低成本OTP语音方案选型-wtn6020唯创知音

一、开发背景&#xff1a; 随着农业现代化的不断推进&#xff0c;鸟类对农作物的侵扰问题愈发严重。传统的驱鸟方法&#xff0c;如人工驱赶或使用化学药剂&#xff0c;不仅效率低下&#xff0c;而且可能对环境造成污染。因此&#xff0c;开发一种高效、环保、低成本的驱鸟器成…

考研日常记录(upd 24.4.24)

由于实在太无聊了 &#xff0c; 所以记录以下考研备考日常 &#xff0c; 增加一点成就感 &#xff0c; 获得一点前进动力。 文章目录 2024.4.18 周四课程情况&#xff1a;时间规划&#xff1a; 2024.4.19 周五课程情况&#xff1a;时间规划&#xff1a; 2024.4.20 周六2024.4.2…

RK3588构建ubuntu22.04根文件系统

前言 RK系列的平台提供了buildroot和debian的系统&#xff0c;使用脚本可以直接构建出来&#xff0c;但是没有提供ubuntu的系统&#xff0c;很多厂商只提供一个rootfs.img的固件包&#xff0c;没有将方法开源出来。本文实现了从ubuntu官网开始构建一个ubuntu22.04根文件系统。…

SSTV音频转图片

SSTV工具有很多&#xff0c;这里使用RX-SSTV慢扫描工具 下载安装 RX-SSTV解码软件 下载地址&#xff1a;https://www.qsl.net/on6mu/rxsstv.htm 一直点下一步&#xff0c;安装成功如下图: 虚拟声卡e2eSoft 由于SSTV工具是根据音频传递图片信息&#xff0c;正常解法需要一…

人耳的七个效应

1、掩蔽效应 • 人们在安静环境中能够分辨出轻微的声音&#xff0c;即人耳对这个声音的听域很低&#xff0c;但在嘈杂的环境中轻微的声音就会被淹没掉&#xff0c;这时将轻微的声音增强才能听到。 • 这种在聆听时&#xff0c;一个声音的听阈因另一声音的出现而提高的现象&…

本地修改localhost--手把手

找到本地hosts文件 1、C:\Windows\System32\drivers–快捷键ctrlR,输入drivers 2、点击etc目录&#xff0c;找到hosts文件&#xff0c;右键使用记事本方式打开编辑 3、添加自己想得到的域名【只能在本地使用】 127.0.0.1 eureka7001.com 127.0.0.1 eureka7002.com 127.0.0.…

面试:JVM内存结构

一、Java代码的运行步骤 一段Java代码先会被反编译为Java字节码&#xff0c;当执行java命令时&#xff0c;JVM虚拟机会被创建出来&#xff0c;并会创建一个main主线程来执行主方法。 二、JVM的内存结构有哪些&#xff1f; 1、方法区&#xff1a;&#xff08;线程共享&#xff…

Linux交换空间的创建使用

交换空间&#xff1a; 换出&#xff1a;将内存中不常用&#xff08;冷数据&#xff09;的放去硬盘里 换出&#xff1a;内存要使用这部分数据时&#xff0c;将硬盘的这部分数据放入内存 在内存和硬盘上用来交换数据的空间就是交换空间 创建交换空间的步骤 1.去磁盘上创建一个分…

Linux中的高级IO函数(一)pipe socketpair dup

Linux提供了很多高级的I/O函数。它们并不像Linux基础I/O函数&#xff08;比如open和read&#xff09;那么常用&#xff08;编写内核模块时一般要实现这些I/O函数&#xff09;&#xff0c;但在特定的条件下却表现出优秀的性能。这些函数大致分为三类&#xff1a; 用于创建文件描…

Mongodb语法使用说明(含详细示例)

点击下载《Mongodb语法使用说明&#xff08;含详细示例&#xff09;》 1. 前言 MongoDB是一款高性能、开源、面向文档的NoSQL数据库&#xff0c;它使用类似JSON的BSON格式存储数据&#xff0c;提供了灵活的数据模型和强大的查询功能。本文将详细介绍MongoDB数据库的基本增删改…

CSS常用属性之(列表、表格、鼠标)属性,(如果想知道CSS的列表、表格、鼠标相关的属性知识点,那么只看这一篇就足够了!)

前言&#xff1a;在学习CSS的时候&#xff0c;必不可少的就要学习选择器和常见的属性&#xff0c;而本篇文章讲解的是CSS中的列表、表格、背景、鼠标属性。 ✨✨✨这里是秋刀鱼不做梦的BLOG ✨✨✨想要了解更多内容可以访问我的主页秋刀鱼不做梦-CSDN博客 大致了解一下本篇文章…

new String和直接赋值的一些问题

分析1 我们先看以下代码&#xff1a; String str1 "abc"; // 在常量池中String str2 new String("abc"); // 在堆上System.out.println(str1 str2)以上结果的输出是什么&#xff1f; 输出&#xff1a;false 前置知识&#xff1a; 在JVM中&#xff0c…

VForm3的文件上传后的一种文件回显方式

更多ruoyi-nbcio功能请看演示系统 gitee源代码地址 前后端代码&#xff1a; https://gitee.com/nbacheng/ruoyi-nbcio 演示地址&#xff1a;RuoYi-Nbcio后台管理系统 http://122.227.135.243:9666/ 更多nbcio-boot功能请看演示系统 gitee源代码地址 后端代码&#xff1a…