c++进阶(3)

C++进阶3:Makefile入门 0、作用 Makefile文件告诉Make怎样编译和连接成一个程序。 1、Makefiile基本语法与执行 1.1 示例 编译一个单文件HelloWorld.cpp (1)编写Makefile HelloWorld : HelloWorld.cpp g++ HelloWorld.cpp -o HelloWorld clean : rm HelloWorld (2)编译 make (3)清空 make clean 1.2 构成 Makefile主要由多条规则构成,每条规则由三部分构成:目标(ta 阅读详情

通过thread类编写C++多线程程序

C++语言层面thread

windows ---- createThread
linux ---- pthread_create

一:怎么创建启动一个线程
std::thread定义一个线程对象,传入线程所需要的线程函数和参数
线程自动

#include<iostream>
#include<thread>
void threadHandle1(int t)
{
	std::this_thread::sleep_for(std::chrono::seconds(t));
	std::cout << "hello thread1" << std::endl;
	std::this_thread::sleep_for(std::chrono::seconds(t));
}

int main()
{
	//定义一个线程对象,传入一个线程函数
	std::thread t1(threadHandle1,3);

	//主线程等待子线程结束,主线程继续往下运行
	t1.join();

	//把子线程设置为分离线程
	t1.detach();

	std::cout << "main thread done" << std::endl;

	return 0;
}

二:子线程如何结束
子线程函数运行完成,线程就结束了

三:主线程如何处理子线程
t.join():等待t线程结束,当前线程继续往下运行
t.detach():把t线程设置为分离线程,主线程结束,整个进程结束,所有子线程都自动结束了!

线程间互斥-mutex互斥锁和lock_guard

C++ thread 模拟车站三个窗口卖票程序

#include<iostream>
#include<thread>
#include<list>
using namespace std;


int counts = 100;

void sellTicket(int index)
{
	while (counts > 0)
	{
		cout << "窗口" << index<<"卖出第:"<<counts<<"张票" << endl;
		counts--;
		std::this_thread::sleep_for(std::chrono::microseconds(100));
	}
}

int main()
{
	list<std::thread> tlist;


	for (int i = 1; i <= 3; ++i)
	{
		tlist.push_back(std::thread(sellTicket, i));
	}

	for (std::thread& t : tlist)
	{
		t.join();
	}

	return 0;
}

在这里插入图片描述

由于线程都是并行,可以看出存在一些错误。

多线程程序
存在竞态条件:多线程程序执行的结果是一致的,不会随着cpu对线程不同的调度顺序,而产生不同的运行结果

在添加互斥变量(锁)后

//全局互斥锁
std::mutex mtx;

void sellTicket(int index)
{
	mtx.lock();
	while (counts > 0)
	{
		cout << "窗口" << index<<"卖出第:"<<counts<<"张票" << endl;
		counts--;
		std::this_thread::sleep_for(std::chrono::microseconds(100));
	}
	mtx.unlock();
}

在这里插入图片描述
没有任何问题了

但是仔细想想?这不是串行运算吗?等于其实只有一个窗口在卖票啊

所以是错的!
正确代码:

	while (counts > 0)
	{
		mtx.lock();
		cout << "窗口" << index<<"卖出第:"<<counts<<"张票" << endl;
		counts--;
		mtx.unlock();
		std::this_thread::sleep_for(std::chrono::microseconds(100));
	}

当然这里也会出现其他问题,比如会出现卖0,-1张票的情形。
所以要再加一层判断

while (counts > 0)
{
	mtx.lock();
	if (counts > 0)
	{	
	cout << "窗口" << index<<"卖出第:"<<counts<<"张票" << endl;
	counts--;
    }
	mtx.unlock();
	std::this_thread::sleep_for(std::chrono::microseconds(100));
}

提问:如果程序发生了不正常运行,比如在if语句内直接return了,那么就没有unlock操作,锁就直接废了。

所以我们引入lock_guard和unique_lock

		lock_guard<std::mutex> lock(mtx);
		unique_lock<std::mutex> lock(mtx);

线程间的互斥=》mutex=》用lock_guard封装mutex

线程间同步通信-生产者消费者模型

多线程编程有两个问题:
一个是线程间的互斥
竞态条件=》临界区代码段=》保证原子操作=》互斥锁mutex — 轻量级的无锁机制 CAS
另一个是线程间的同步通信

示例代码:一个普通的生产者消费者模型(未添加同步通信)

#include<iostream>
#include<thread>
#include<mutex>
#include<queue>
using namespace std;


std::mutex mtx;


class QUE {

public:
	void put(int val)//生产物品
	{
		lock_guard<std::mutex> guard(mtx);
		que.push(val);
		cout << "生产者 生产:" << val<<"号物品" << endl;
	}

	int get()//消费物品
	{
		lock_guard<std::mutex> guard(mtx);
		int val = que.front();
		que.pop();
		cout << "消费者 消费:" << val << "号物品" << endl;
		return val;
	}


private:
	queue<int> que;
};

void producer(QUE* que)
{
	for (int i = 1; i <= 10; ++i)
	{
		que->put(i);
		std::this_thread::sleep_for(std::chrono::milliseconds(100));
	}
}


void consumer(QUE* que)
{
	for (int i = 1; i <= 10; ++i)
	{
		que->get();
		std::this_thread::sleep_for(std::chrono::milliseconds(100));
	}
}
int main()
{
	
	QUE que;
	std::thread t1(producer,&que);
	std::thread t2(consumer ,&que);
	

	t1.join();
	t2.join();
	return 0;
}

在这里插入图片描述
可以看出,当消费者比生产者快时,消费者无法获取产品

#include<iostream>
#include<thread>
#include<mutex>
#include<queue>
#include<condition_variable>
using namespace std;


std::mutex mtx;//定义互斥锁,做线程间的互斥操作
condition_variable cv;//定义条件变量做线程间的通信

class QUE {

public:
	void put(int val)//生产物品
	{
		lock_guard<std::mutex> guard(mtx);
		if (!que.empty())
		{
			//que不为空,生产者应该通知消费者去消费,消费完了,再继续生产
			//生产者线程应该进入阻塞状态,并且把mtx互斥锁释放掉
			unique_lock<std::mutex> lck(mtx);
			cv.wait(lck);

		}
		que.push(val);
		//通知其他所有线程
		cv.notify_all();
		cout << "生产者 生产:" << val<<"号物品" << endl;
	}

	int get()//消费物品
	{
		lock_guard<std::mutex> guard(mtx);
		while (que.empty())
		{
			//发现que是空的,通知生产者线程先生产物品
			// 1. 进入等待状态,2把互斥锁释放
			unique_lock<std::mutex> lck(mtx);
			cv.wait(lck);
		}

		int val = que.front();
		que.pop();
		cout << "消费者 消费:" << val << "号物品" << endl;
		return val;
	}


private:
	queue<int> que;
};

void producer(QUE* que)
{
	for (int i = 1; i <= 10; ++i)
	{
		que->put(i);
		std::this_thread::sleep_for(std::chrono::milliseconds(100));
	}
}


void consumer(QUE* que)
{
	for (int i = 1; i <= 10; ++i)
	{
		que->get();
		std::this_thread::sleep_for(std::chrono::milliseconds(100));
	}
}
int main()
{
	
	QUE que;
	std::thread t1(producer,&que);
	std::thread t2(consumer ,&que);
	

	t1.join();
	t2.join();
	return 0;
}

再谈lock_guard和unique_lock

unique_lock 和 condition_variable

  1. lock_guard和unique_lock
	//利用智能指针的概念,在构造函数在能主动的获取锁
	//当对象出作用域时,调用析构函数再释放
	//不能使用在函数调用过程中
	lock_guard<std::mutex> guard(mtx);

unique_lock只提供了右值引用的赋值和拷贝构造
可以使用在函数调用过程中

std::mutex mtx;

std::condition_variable cv;

int main()
{
	
	//利用智能指针的概念,在构造函数在能主动的获取锁
	//当对象出作用域时,调用析构函数再释放
	unique_lock<std::mutex> lck(mtx);
	cv.wait(lck);// =>1. 使线程进入等待状态 2.lck.unlock还可以把mtx给释放掉
  1. condition_variable wait和notify_all方法
    cv.notify_all();
    通知在cv上等待的线程,条件成立了,起来干活了
    其他在cv上等待的线程,收到通知,从阻塞状态=》等待状态=》获取互斥锁=》程序继续运行

基于CAS操作的atomic原子类型

互斥锁是比较重的,如果临界区代码做的事情稍微的复杂,多。就用互斥锁

但是如果只是简单的+±-操作,可以用CAS轻量化锁来实现,

实例代码:无锁队列=》CAS实现

volatile std::atomic_bool isReady = false;
volatile std::atomic_int counts = 0;

void task()
{
	while(!isReady())
	{
		std::this_thread::yield();//线程出让当前的cpu时间片
		//等待下一次调度
	}

	for(int i = 0;i<100;++i)
	{
		counts++;
	}
}

int main()
{
	list<std::thread> tlist;
	for(int i = 0;i<10;++i)
	{
		tlist.push_back(std::thread(task));
	}
	isReady = true;
	
	std::this_thread::sleep_for(std::chrono::seconds(3));
	for(std::thread &t : tlist)
	{
		t.join();
	}
	
	cout<<counts<<endl;
}
C++ 进阶教程3 暴力枚举 暴力枚举算法是通过列举问题的所有可能情况,然后通过直接判断来得到问题的答案。这种方法常常不依赖于任何优化技巧,完全依靠计算机强大的计算能力进行计算。暴力枚举是解决问题的“暴力”方法,通常被用作其他更优化算法的对比基准。例如,在处理一些排列组合问题时,暴力枚举方法通常会通过列举所有的排列组合方式,然后依次进行判断,找到符合条件的解。暴力枚举是一种简单且直观的算法思想,适用于解决各种组合、排列等问题。尽管暴力枚举通常不具备高效性,但它可以作为许多问题的基础解法,尤其是在问题规模较小或解空间有限的情况下。 阅读详情

相关推荐

【C++进阶之路】C++防止头文件被重复引入的3种方法!

在之前我们详细介绍了 C 语言中如何使用宏定义(#ifndef / #define / #endif)来有效避免头文件被重复 #include,此方式在 C++ 多文件编程中也很常用。 举个例子,如下是一个 C++ 项目,其内部含有 school.h 和 student.h 这 2 个头文件以及 main.cpp 源文件,其各自包含的代码为: //student.h class Student { //...... }; //school.h #include "student.h"..

HUYA69的博客 750

C++进阶:3_二叉树进阶

因此本节借二叉树搜索树,对二叉树部分进行收尾总结。

Open__Ai的博客 1575

微磁模拟实战指南:从OOMMF安装到首个3D模型运行

本文详细介绍了OOMMF微磁模拟软件的安装与使用,从环境配置到首个3D模型运行的完整教程。涵盖Windows、Linux和macOS平台的安装避坑指南,以及MIF文件编写、并行计算优化等实战技巧,帮助研究人员快速掌握这一开源工具在磁畴壁运动、磁滞回线等微磁学问题中的应用。

weixin_29294597的博客 153

C++编程:进阶阶段—3函数进阶

PS:占位参数也可以有默认值,如int func(int a,int =10)C++中函数的形参列表里可以有占位参数,用来占位,调用函数时必须填补该位置。PS:如果某个形参有了默认值,则该形参及其后面的形参都必须有默认值。如果函数声明有默认参数,则函数实现不能有默认参数。错误示例:函数声明有默认参数,函数实现也有默认参数。语法:返回值类型 函数名(参数=默认值){}PS:函数的返回值不可作为函数重载的条件。C++中,函数的形参是可以有默认值的。函数重载碰到默认参数。错误示例:函数重载时定义默认参数。

qq_52737907的博客 408

MagicDraw-需求图

MagicDraw学习笔记

木头征的博客 4136

RS422总线

①.RS422发送驱动器无高阻三态(浮空)状态RS422收发芯片,发送脚固定强驱动输出,永远钳位 A/B 差分电平,不能关断、不能悬空,只要芯片上电,TX+、TX - 就强制输出互补电压(一路高、一路低)。多条发送并联在同一组差分线上:两个驱动器一个拉 A 高、一个拉 A 低,B 线反向拉扯 → 电源→地大电流对灌,瞬间芯片过流发烫烧毁、总线电平短路错乱。注意:RS485 带DE/RE 收发使能脚:不用发送时可关闭驱动器,输出变成高阻浮空,让出总线,因此能多节点分时发送。③.多点通信方式,一主多从方式;

zhuimeng_1234的博客 223

C++进阶(3)——C++11基础

C++11是C++语言的重要更新版本,于2011年发布,引入了多项关键改进。文章主要介绍了C++11的新特性:1. 列表初始化统一了初始化语法,支持内置类型和自定义类型;2. initializer_list类简化了容器初始化;3. auto和decltype实现了类型自动推导;4. nullptr解决了空指针歧义问题;5. 范围for循环简化了容器遍历。此外还介绍了新增容器(array、forward_list等)和字符串转换函数(to_string/stoi等)。

2301_80065652的博客 3575

【OTDR曲线工具箱】04 批量生成sor文件

软件下载地址:http://120.78.150.214:9999/isora.zip 通过一个测试数据sor文件,批量生成多个测试文件(基于Bellcore GR196及SR-4731标准)。主要考虑哪些问题: 1、sor测试是否符合要求(主要是衰减系数) 2、多个文件的创建时间 3、文件必须随机处理,不要看省去一模一样。 来说说怎么操作的 第一步:选择参考曲线 参考曲线的选择由两种方法:从文件中导入、从曲线库中选择。 其中曲线库分为公共库和个人库两种,平时浏览的sor文件都可以加载到库中。 第二步

夜晚是传说的博客 5271

C++进阶知识3.0

这篇文章摘要了C++进阶知识中的多个关键点: set与map封装:使用仿函数实现自定义排序,建议无序场景用unordered_set 类型处理:typename声明迭代器,typeid().name()获取原始类型名(如long输出l) 字符串输出陷阱:char*被cout当作C字符串处理,需强转为(void*)打印地址 进程终止:kill -9强制终止可能泄漏资源,Ctrl+C更安全 常量优化:constexpr要求编译期确定值;const比#define更节省空间(避免多份数据拷贝) 错误处理:通过er

深入探究的博客 444

USB PD Sink HUSB238A可应用于24V电动风扇/电动工具

HUSB238A在I2C模式下,可申请PD3.1 EPR AVS功率,可申请24V电压直接给电机供电

Hynetek的博客 759

51单片机8×8LED点阵屏动态显示取模汉字+ Proteus仿真

51单片机8×8LED点阵屏动态显示取模汉字+ Proteus仿真 Proteus仿真 取模软件:pctolcd2002 程序代码 #include<reg52.h> unsigned char code taba[]={0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f};//行选 unsigned char code tabb[]={0x08,0x49,0x49,0x7F,0x08,0x49,0x49,0x7F};//选列"出" unsigned cha

perseverance51博客 8057

通达信指标公式(4)MACD指标3个进阶版本与C++源代码(值得收藏)

今天主要分享我在实战中,使用MACD的3个进阶版本(如下图),介绍每个版本的由来,如何一步步优化、调整,从而去除一些干扰的信号(去噪),最终提供相对正确率较高的入场信号。

码上助君的专栏 5555

基于 STM32 单片机的实验室多参数安全监测系统设计与实现

本系统以 STM32F103C8T6 单片机为核心,集成温湿度监测、烟雾检测、气体泄漏报警、人体移动监测等功能模块,通过 OLED 显示屏实时显示数据,并支持 Wi-Fi 远程传输。系统可对实验室异常环境参数(如高温、烟雾、燃气泄漏)及非法入侵实时报警,保障实验室安全。本文从硬件选型、电路设计到软件实现提供了完整的实验室安全监测系统方案,可根据实际需求调整传感器类型和通信方式,适用于科研、教学等场景的环境安全防护。:可增加继电器模块控制通风设备或切断电源,集成 ZigBee 组网实现多实验室联动监测。

weixin_42300449的博客 1285

【C++进阶(3):进阶二叉树】

前面在C初阶数据结构学习阶段,我对二叉树建立了初步认识,并总结了运用其解决一些问题的方法,但实际上二叉树是一个十分值得深入研究的结构,在有了一定的C++基础的条件下,我将基于C++继续总结二叉树相关的一些更复杂的问题,本篇涉及到的二叉搜索树主要是为后面学习map和set的特性而准备,同时二叉树部分在笔试面试也是考察的重要部分之一,笔记最后也会总结一些复杂的二叉树相关OJ题

weixin_56235740的博客 167

微信小程序 - request:fail errcode:10011 cronet_error_code:0 error_msg:network change, cancel all task请求报错

微信小程序请求接口,报request:fail errcode:10011是什么原因,uniapp微信小程序request:fail errcode:10011 cronet_error_code:0 error_msg:network change, cancel all task,微信小程序wx.request常规访问接口,有个别用户的接口请求频繁报错,UniApp 微信小程序uni.request请求接口报错:request:fail errcode:10011 的原因分析与解决方案,苹果ios,安卓

🏆 前端领域优质创作者 4567

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

C++ Template 进阶指南-3

2.2 模板世界的If-Then-Else:类模板的特化与偏特化 2.2.1 根据类型执行代码 前一节的示例提出了一个要求:需要做出根据类型执行不同代码。要达成这一目的,模板并不是唯一的途径。比如之前我们所说的重载。如果把眼界放宽一些,虚函数也是根据类型执行代码的例子。此外,在C语言时代,也会有一些技法来达到这个目的,比如下面这个例子,我们需要对两个浮点做加法, 或者对两个整数做乘法: s...

勤学如春起之苗,不见其增,日有所长! 辍学如磨刀之石,不见其损,日有所亏! 406

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

C++进阶—>多态(3个问题了解它)

该模块属于C++进阶内容,通过三个问题(是什么?——>怎么实现??——>为什么可以这样???来理解多态这个陌生的词语,以及底层原理?在知道多态是怎样实现的,就需要知道虚函数表、虚函数表这两个词语!

2301_78704584的博客 962

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

C++进阶(3)二叉树进阶

C++进阶(3)二叉树进阶

2301_79907403的博客 510

C++ 进阶3-2 题解

这些练习题的解决方案使用了原生数组、循环和基础的递归方法,避免了使用高级数据结构(如vector),使得代码更加简洁易懂。虽然这种方法在效率上不如现代 C++ 中使用 STL 容器,但它为初学者提供了清晰的理解基础,并帮助熟悉常见的算法思想(如暴力枚举、回溯和排列组合等)。

yjs20135869的博客 461

【C++ 20进阶(3):初始化 Initializer

本次主要介绍C++变量相关一些初始化,重点为C++20新增初始方式,之前版本有的重要内容也会有介绍。

weixin_44259356的博客 682

CPP -- W3 类和对象进阶

3 类和对象进阶 3.1 复制构造函数(拷贝构造函数) 1). 基本概念 X::XX(X&) 或 X::X(cons X&) 用户未定义构造函数时类有2个构造函数,分别为:缺省的无参数的构造函数,缺省的复制构造函数(参数为该类的对象); 用户定义了自己的复制构造函数时,将不存在默认的复制构造函数。zpcj?这是会存在缺省的无参数的构造函数吗? 不允许有形如X::X(X)的构造函数。

shuiyuezhou的专栏 646

C++_进阶篇_3

复制构造函数 需求说明 自定义String(字符串)类,以简化字符串的操作

qq_40433783的博客 182

c++进阶3:Makefile-入门

0. 作用 Makefile文件告诉Make怎样编译和连接成一个程序。 1. Makefile基本语法与执行 1.1 示例 1.编译一个单文件HelloWorld.cpp #include <iostream> using namespace std; int main(){ cout << "Hello World" << endl; } 编写Makefile vim Makefile Hello:Hello.cpp #1.目标 2.源 g++

weixin_42278523的博客 374

C++ 进阶3-1 练习

你需要找到一条路径,访问每个城市一次并返回起始城市,使得总路径距离最短。:通过暴力枚举所有可能的城市排列,计算路径长度并找出最短路径。使用暴力枚举的方式,遍历所有的城市排列,找到最短路径。:暴力枚举所有的子集,可以使用递归或迭代的方式。不使用C++ STL中的现成方法来查找答案。子集中的元素可以是原数组中的元素的任意组合。不使用C++ STL中的现成方法来生成排列。,返回数组的所有可能的子集(幂集)。使用暴力枚举的方法,遍历所有可能的。不使用C++ STL中的现成方法。的两个数,并返回它们的下标。

yjs20135869的博客 623

C++进阶3:二叉搜索树从原理到 C++ 实现

二叉搜索树(Binary Search Tree,简称 BST)是数据结构领域的 “基石”,它不仅是面试高频考点,更是很多工业级组件(如 C++ std::map/std::set、数据库索引)的底层基础。本文将严格遵循你的大纲,从概念、性能、核心操作、完整实现到key/value 场景实战,用 C++ 代码带你彻底掌握 BST 的原理与工程应用。任意节点的左子树所有节点值 < 当前节点值;任意节点的右子树所有节点值 > 当前节点值;左、右子树本身也必须是二叉搜索树。

chen060603的博客 769

CANN AI Core Error 排障终极指南:msaicerr工具定位方法与6个典型案例

在 CANN(异构计算架构)环境中,NPU 任务报错退出时终端常出现 **EZ9999 / EZ2001** 错误码,日志中包含 "the error is aicore error"——这就是典型的 **AI Core Error 故障**。本文将手把手教你使用 **msaicerr 工具**快速定位 AI Core Error 的关键原因,并给出 6 个高频典型案例的排查方法,让你从"报错看不

gitblog_00254的博客 981
上一篇: c++进阶(2)
下一篇: go debug日记:protoc -I . helloworld.proto --go_out=plugins=grpc:.错误debug
fcopy
博客等级 码龄6年 252粉丝 30原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值