当前位置: 首页 > news >正文

线程同步与互斥

目录

前言:基于多线程不安全并行抢票

一、线程互斥锁 mutex

1.1 加锁解锁处理多线程并发

 1.2 如何看待锁

1.3 如何理解加锁解锁的本质

1.4 C++RAII方格设计封装锁

前言:基于线程安全的不合理竞争资源

二、线程同步

1.1 线程同步处理抢票

1.2 如何理解"条件变量"

1.3 如何理解条件变量函数需要传锁参数


前言:基于多线程不安全并行抢票

#include<iostream>
#include<pthread.h>
#include<unistd.h>
#define NUM 10
using namespace std;
int global_ticket = 10000;void* GetTicket(void* args)
{char* pc =(char*)args;while(global_ticket > 0){usleep(123);cout << pc <<" ticket= " << global_ticket--<<endl;}delete pc;
}int main()
{for(int i=0;i<NUM;i++){char* pc = new char[64];pthread_t tid;snprintf(pc,128,"thread %d get a ticket",i+1);pthread_create(&tid,nullptr,GetTicket,pc);}while(true);return 0;
}

发现结果和我们代码的预想不一样,出现了票已经售完却仍抢票的现象!

为什么会出现这种现象? 原来Linux线程是轻量级进程,是CPU调度的基本单位,所以每个线程在CPU中都有自己的时间片,所以线程会在CPU上不断的切换,这样有可能某个线程在运行函数过程中突然被截胡了!然后再轮到它的时候继续运行!那结合本次抢票逻辑,可能某个线程首先while条件满足了拥有了抢票资格,但是没有开始抢票就被截胡了,下次回来后它仍拥有资格拿到一张票,可是之前票已经被抢完了!所以他其实没有资格了,但是因为不安全访问全局变量,使得它可以继续拿到票!

这样因为线程的特性原因使得我们无法安全的访问一个全局共享变量!所以我们要使线程互斥访问该变量,当某一个线程访问的时候,其他线程无法干预截胡,该线程必须将自己的代码逻辑执行完或者不执行(要么不做,做就必须完成!) 这就叫线程的互斥!那些代码区域称作为临界区!


一、线程互斥锁 mutex

1.1 加锁解锁处理多线程并发


#include<iostream>
#include<pthread.h>
#include<unistd.h>
#include<string>
#include<vector>
using namespace std;int tickets = 10000;class Thread_data
{
public:string name_;pthread_mutex_t* mutex_;
};void* pthread_route(void* args)
{Thread_data* pt = static_cast<Thread_data*>(args);while(true){//加锁pthread_mutex_lock(pt->mutex_);if(tickets > 0){cout << pt->name_<<"get a ticket, tickets = "<<--tickets <<endl;//解锁pthread_mutex_unlock(pt->mutex_);//!!!抢完票后的操作 如果没有这个步骤,该线程会一直占用CPU执行抢票逻辑!usleep(1234);}else{//注意这个逻辑里也要解锁!pthread_mutex_unlock(pt->mutex_);break;}}delete pt;
}#define NUM 5
int main()
{vector<pthread_t> vp(NUM); char buffer[64];//锁初始化//1.锁类型 + 初始化函数//2.全局变量 pthread_mutex_t mutex =宏(PTHREAD_MUTEX_INITIALIZER)pthread_mutex_t mutex;pthread_mutex_init(&mutex,nullptr);for(int i=0;i<NUM;i++){pthread_t tid;snprintf(buffer,sizeof(buffer),"thread %d ",i+1);Thread_data* pt = new Thread_data();pt->mutex_ = &mutex;pt->name_ = buffer;pthread_create(&(vp[i]),nullptr,pthread_route,pt);}for(const auto& tid : vp){pthread_join(tid,nullptr);}return 0;
}

现在结果看到不会出现票数为负数的情况,锁保护了我们的共享资源tickets! 


 1.2 如何看待锁

a.锁本身被线程共享,是一个共享资源,锁保护全局变量,锁本身也需要被保护

b.加锁和解锁的过程必须是安全的,也就说加锁的过程必须是原子性的!

c.如果锁申请成功,就继续向后执行,如果不成功则执行流阻塞!

d.谁持有锁,谁进入临界区!

1.3 如何理解加锁解锁的本质

首先理解这个过程我们首先要明确关于CPU的一个知识就是寄存器:

寄存器只有一套,但寄存器被线程所共享,寄存器存储的内容只被当前线程所拥有!

加锁的过程:

① 将内存中的线程变量al的值0放入CPU的寄存器内

② 然后将寄存器内的0与内存变量mutex值交换,其中mutex的为1,这样mutex = 0

③ 最后通过判断al在寄存器的值来判断是否能申请锁成功!>0 成功 else 不成功

此时当某个线程完成了前面两个步骤,其他线程申请锁时将等于0的mutex与al交换,al依旧是0所以他无法申请锁!这样就可以保证只有一个线程申请到锁!

解锁的过程:就是把寄存器al的1存入mutex中即可!

这里swap过程很重要,它是一条汇编完成!其本质是将共享变量放入到我们的上下文中!


1.4 C++RAII方格设计封装锁

//mutex.hpp
#include<iostream>
#include<pthread.h>class Mutex
{
public:Mutex(pthread_mutex_t* lock_p = nullptr):lock_p_(lock_p){}void lock(){if(lock_p_)pthread_mutex_lock(lock_p_);}void unlock(){if(lock_p_)pthread_mutex_unlock(lock_p_);}
private:pthread_mutex_t* lock_p_;
};class LockGuard
{
public:LockGuard(pthread_mutex_t* mutex):mutex_(mutex){mutex_.lock();}~LockGuard(){mutex_.unlock();}
private:Mutex mutex_;
};

💡该封装利用局部类变量的构造析构随着作用域自动调用使得加锁解锁自动调用! 


前言:基于线程安全的不合理竞争资源

#include<iostream>
#include<pthread.h>
#include<unistd.h>
#include<string>
#include<vector>
#include"mutex.hpp"
using namespace std;int tickets = 10000;class Thread_data
{
public:string name_;pthread_mutex_t* mutex_;
};void* pthread_route(void* args)
{Thread_data* pt = static_cast<Thread_data*>(args);while(true){//加锁//pthread_mutex_lock(pt->mutex_);LockGuard lock(pt->mutex_);if(tickets > 0){cout << pt->name_<<"get a ticket, tickets = "<<--tickets <<endl;//解锁//pthread_mutex_unlock(pt->mutex_);//!!!抢完票后的操作 如果没有这个步骤,该线程会一直占用CPU执行抢票逻辑!}else{//注意这个逻辑里也要解锁!//pthread_mutex_unlock(pt->mutex_);break;}usleep(1000);}delete pt;
}#define NUM 5
int main()
{vector<pthread_t> vp(NUM); char buffer[64];//锁初始化//1.锁类型 + 初始化函数//2.全局变量 pthread_mutex_t mutex =宏(PTHREAD_MUTEX_INITIALIZER)pthread_mutex_t mutex;pthread_mutex_init(&mutex,nullptr);for(int i=0;i<NUM;i++){pthread_t tid;snprintf(buffer,sizeof(buffer),"thread %d ",i+1);Thread_data* pt = new Thread_data();pt->mutex_ = &mutex;pt->name_ = buffer;pthread_create(&(vp[i]),nullptr,pthread_route,pt);}for(const auto& tid : vp){pthread_join(tid,nullptr);}return 0;
}

 可以肯定的是线程资源是安全的了,但是却是一个线程在疯狂抢票,这样的行为不是错误的,但不合理!我们需要每个线程都有机会,所以引入线程同步,利用同步"信号"与等待队列来实现线程公平的竞争!


二、线程同步

1.1 线程同步处理抢票


#include <iostream>
#include <pthread.h>
#include <unistd.h>
#include <string>
using namespace std;int tickets = 100;//初始化锁和条件变量
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;void *pthread_route(void *args)
{string name = (char *)args;while (true){//加锁pthread_mutex_lock(&mutex);//条件等待信号唤醒pthread_cond_wait(&cond, &mutex);if (tickets > 0)cout << name << "get a ticket, tickets = " << --tickets << endl;pthread_mutex_unlock(&mutex);}
}int main()
{pthread_t tid1, tid2, tid3;pthread_create(&tid1, nullptr, pthread_route, (void *)"thread 1 ");pthread_create(&tid2, nullptr, pthread_route, (void *)"thread 2 ");pthread_create(&tid3, nullptr, pthread_route, (void *)"thread 3 ");// 主线程负责每隔一秒信号唤醒条件变量while (true){sleep(1);pthread_cond_signal(&cond);cout << "main wake up a thread ..." << endl;}pthread_join(tid1, nullptr);pthread_join(tid2, nullptr);pthread_join(tid3, nullptr);return 0;
}


 1.2 如何理解"条件变量"

首先明确一下整个代码逻辑:
a.先加锁,保护临界资源

b.条件等待,等待条件满足信号唤醒,否则阻塞!

c.解锁

当线程条件阻塞,会被放入条件队列中等待!当有信号唤醒的时候,从队列中一个一个得出!

1.3 如何理解条件变量函数需要传锁参数

因为等待队列被线程共享,为了保证线程入队列安全,所以需要加锁保护线程安全! 

相关文章:

线程同步与互斥

目录 前言&#xff1a;基于多线程不安全并行抢票 一、线程互斥锁 mutex 1.1 加锁解锁处理多线程并发 1.2 如何看待锁 1.3 如何理解加锁解锁的本质 1.4 CRAII方格设计封装锁 前言&#xff1a;基于线程安全的不合理竞争资源 二、线程同步 1.1 线程同步处理抢票 1.2 如何…...

电子词典dictionary

一、项目要求&#xff1a; 1.登录注册功能&#xff0c;不能重复登录&#xff0c;重复注册。用户信息也存储在数据库中。 2.单词查询功能 3.历史记录功能&#xff0c;存储单词&#xff0c;意思&#xff0c;以及查询时间&#xff0c;存储在数据库 4.基于TCP&#xff0c;支持多客户…...

【python爬虫】10.指挥浏览器自动工作(selenium)

文章目录 前言selenium是什么怎么用设置浏览器引擎获取数据解析与提取数据自动操作浏览器 实操运用确认目标分析过程代码实现 本关总结 前言 上一关&#xff0c;我们认识了cookies和session。 分别学习了它们的用法&#xff0c;以及区别。 还做了一个项目&#xff1a;带着小…...

QT文件对话框,将标签内容保存至指定文件

一、主要步骤 首先&#xff0c;通过getSaveFileName过去想要保存的文件路径及文件名&#xff0c;其次&#xff0c;通过QFile类实例化一个文件对象&#xff0c;再读取文本框中的内容&#xff0c;最后将读取到的内容写入到文件中&#xff0c;最后关闭文件。 1.txt即为完成上述操作…...

C#,《小白学程序》第十一课:阶乘(Factorial)的计算方法与代码

1 文本格式 /// <summary> /// 阶乘的非递归算法 /// </summary> /// <param name"a"></param> /// <returns></returns> private int Factorial_Original(int a) { int r 1; for (int i a; i > 1; i--) { …...

MySQL 数据库常用命令大全(完整版)

文章目录 1. MySQL命令2. MySQL基础命令3. MySQL命令简介4. MySQL常用命令4.1 MySQL准备篇4.1.1 启动和停止MySQL服务4.1.2 修改MySQL账户密码4.1.3 MySQL的登陆和退出4.1.4 查看MySQL版本 4.2 DDL篇&#xff08;数据定义&#xff09;4.2.1 查询数据库4.2.2 创建数据库4.2.3 使…...

【数学】【书籍阅读笔记】【概率论】应用随机过程概率论模型导论 by Sheldon M.Ross 第一章 概率论引总结与习题题解 【更新中】

文章目录 前言1 第一章 概率论引论 总结1.1 样本空间与事件1.2 定义在事件上的概率1.3 条件概率1.4 独立事件 2 一些有用的重要结论/公式/例题3 重要例题例 1.11 3 习题题解题1题2 4 习题总结 前言 1 第一章 概率论引论 总结 第一章从事件的角度引出样本空间、事件、概率的基本…...

posexplode函数实战总结

目录 1、建表和准备数据 2、炸裂实践 3、错误炸裂方式 4、当字段类型为string&#xff0c;需要split一下 对单列array类型的字段进行炸裂时&#xff0c;可以使用lateral view explode。 对多列array类型的字段进行炸裂时&#xff0c;可以使用lateral view posexplode。 1…...

QTday3(对话框、发布软件、事件处理核心机制)

一、Xmind整理&#xff1a; 二、上课笔记整理&#xff1a; 1.消息对话框&#xff08;QMessageBox&#xff09; ①基于属性版本的API QMessageBox::QMessageBox( //有参构造函数名QMessageBox::Icon icon, //图标const Q…...

el-date-picker限制选择的时间范围

<el-date-pickersize"mini"v-model"dateTime"value-format"yyyy-MM-dd HH:mm:ss"type"datetimerange"range-separator"~"start-placeholder"开始日期"end-placeholder"结束日期":picker-options&quo…...

Scala中的Actor模型

Scala中的Actor模型 概念 Actor Model是用来编写并行计算或分布式系统的高层次抽象&#xff08;类似java中的Thread&#xff09;让程序员不必为多线程模式下共享锁而烦恼。Actors将状态和行为封装在一个轻量的进程/线程中&#xff0c;但是不和其他Actors分享状态&#xff0c;…...

Java使用pdfbox将pdf转图片

前言 目前比较主流的两种转pdf的方式&#xff0c;就是pdfbox和icepdf&#xff0c;两种我都尝试了下&#xff0c;icepdf解析出来有时候会出现中文显示不出来&#xff0c;网上的解决方式又特别麻烦&#xff0c;不是安装字体&#xff0c;就是重写底层类&#xff0c;所以我选择了p…...

大规模场景下对Istio的性能优化

简介 当前istio下发xDS使用的是全量下发策略&#xff0c;也就是网格里的所有sidecar(envoy)&#xff0c;内存里都会有整个网格内所有的服务发现数据。这样的结果是&#xff0c;每个sidecar内存都会随着网格规模增长而增长。 Aeraki-mesh aeraki-mesh项目下有一个子项目专门用来…...

数字化新零售平台系统提供商,门店商品信息智慧管理-亿发进销存

传统的批发零售业务模式正面临着市场需求变化的冲击。用户日益注重个性化、便捷性和体验感&#xff0c;新兴的新零售模式迅速崛起&#xff0c;改变了传统的零售格局。如何在保持传统业务的基础上&#xff0c;变革发展&#xff0c;成为了业界亟需解决的问题。 在这一背景下&…...

postgresql-窗口函数

postgresql-窗口函数 简介窗口函数的定义分区选项&#xff08;PARTITION BY&#xff09;排序选项&#xff08;ORDER BY&#xff09;窗口选项&#xff08;frame_clause&#xff09; 聚合窗口函数排名窗口函数演示了 CUME_DIST 和 NTILE 函数 取值窗口函数 简介 常见的聚合函数&…...

Revit SDK 介绍:CreateAirHandler 创建户式风管机

前言 这个例子介绍如何通过 API 创建一个户式风管机族的内容&#xff0c;包含几何和接头。 内容 效果 核心逻辑 必须打开机械设备的族模板创建几何实体来表示风管机创建风机的接头 创建几何实体来表示风管机 例子中创建了多个拉伸&#xff0c;下面仅截取一段代码&#xff…...

微信小程序云开发-云函数发起https请求简易封装函数

一、前言 在日常的开发中&#xff0c;经常会遇到需要请求第三方API的情况&#xff0c;例如请求实名认证接口、IP转换地址接口等等。这些请求放在小程序前端的话&#xff0c;就需要把密钥放在客户端&#xff0c;在安全性上没这么高。 因此&#xff0c;一般是放在云函数端去访问…...

深入探索PHP编程:连接数据库的完整指南

深入探索PHP编程&#xff1a;连接数据库的完整指南 在现代Web开发中&#xff0c;与数据库进行交互是不可或缺的一部分。PHP作为一种强大的服务器端编程语言&#xff0c;提供了丰富的工具来连接和操作各种数据库系统。本篇教程将带您了解如何在PHP中连接数据库&#xff0c;执行…...

【Centos8配置节点免密登陆】

登录Centos8 配置免密登录 为什么需要配置免密登录&#xff0c;玩大数据&#xff0c;玩集群的朋友们&#xff0c;都需要使用RPC通讯&#xff0c;完成集群命令同步&#xff0c;数据操作通讯。要实现RPC通讯&#xff0c;就需要配置节点之间的免密登录。 # 配置登录秘钥 ssh-key…...

不可变集合、Lambda表达式、Stream流

不可变集合、Lambda表达式、Stream流 创建不可变集合 不能被修改的集合 应用场景 如果某个数据不能被修改&#xff0c;把它防御性的拷贝到不可变集合中是个很好的实践。 当集合对象被不可信的库调用时&#xff0c;不可变形式是安全的。 创建不可变集合 在List、Set、Map接口中…...

大型活动交通拥堵治理的视觉算法应用

大型活动下智慧交通的视觉分析应用 一、背景与挑战 大型活动&#xff08;如演唱会、马拉松赛事、高考中考等&#xff09;期间&#xff0c;城市交通面临瞬时人流车流激增、传统摄像头模糊、交通拥堵识别滞后等问题。以演唱会为例&#xff0c;暖城商圈曾因观众集中离场导致周边…...

Opencv中的addweighted函数

一.addweighted函数作用 addweighted&#xff08;&#xff09;是OpenCV库中用于图像处理的函数&#xff0c;主要功能是将两个输入图像&#xff08;尺寸和类型相同&#xff09;按照指定的权重进行加权叠加&#xff08;图像融合&#xff09;&#xff0c;并添加一个标量值&#x…...

屋顶变身“发电站” ,中天合创屋面分布式光伏发电项目顺利并网!

5月28日&#xff0c;中天合创屋面分布式光伏发电项目顺利并网发电&#xff0c;该项目位于内蒙古自治区鄂尔多斯市乌审旗&#xff0c;项目利用中天合创聚乙烯、聚丙烯仓库屋面作为场地建设光伏电站&#xff0c;总装机容量为9.96MWp。 项目投运后&#xff0c;每年可节约标煤3670…...

AI编程--插件对比分析:CodeRider、GitHub Copilot及其他

AI编程插件对比分析&#xff1a;CodeRider、GitHub Copilot及其他 随着人工智能技术的快速发展&#xff0c;AI编程插件已成为提升开发者生产力的重要工具。CodeRider和GitHub Copilot作为市场上的领先者&#xff0c;分别以其独特的特性和生态系统吸引了大量开发者。本文将从功…...

淘宝扭蛋机小程序系统开发:打造互动性强的购物平台

淘宝扭蛋机小程序系统的开发&#xff0c;旨在打造一个互动性强的购物平台&#xff0c;让用户在购物的同时&#xff0c;能够享受到更多的乐趣和惊喜。 淘宝扭蛋机小程序系统拥有丰富的互动功能。用户可以通过虚拟摇杆操作扭蛋机&#xff0c;实现旋转、抽拉等动作&#xff0c;增…...

uniapp 实现腾讯云IM群文件上传下载功能

UniApp 集成腾讯云IM实现群文件上传下载功能全攻略 一、功能背景与技术选型 在团队协作场景中&#xff0c;群文件共享是核心需求之一。本文将介绍如何基于腾讯云IMCOS&#xff0c;在uniapp中实现&#xff1a; 群内文件上传/下载文件元数据管理下载进度追踪跨平台文件预览 二…...

​​企业大模型服务合规指南:深度解析备案与登记制度​​

伴随AI技术的爆炸式发展&#xff0c;尤其是大模型&#xff08;LLM&#xff09;在各行各业的深度应用和整合&#xff0c;企业利用AI技术提升效率、创新服务的步伐不断加快。无论是像DeepSeek这样的前沿技术提供者&#xff0c;还是积极拥抱AI转型的传统企业&#xff0c;在面向公众…...

Python爬虫实战:研究Restkit库相关技术

1. 引言 1.1 研究背景与意义 在当今信息爆炸的时代,互联网上存在着海量的有价值数据。如何高效地采集这些数据并将其应用于实际业务中,成为了许多企业和开发者关注的焦点。网络爬虫技术作为一种自动化的数据采集工具,可以帮助我们从网页中提取所需的信息。而 RESTful API …...

「Java基本语法」变量的使用

变量定义 变量是程序中存储数据的容器&#xff0c;用于保存可变的数据值。在Java中&#xff0c;变量必须先声明后使用&#xff0c;声明时需指定变量的数据类型和变量名。 语法 数据类型 变量名 [ 初始值]; 示例&#xff1a;声明与初始化 public class VariableDemo {publi…...

【Java】Ajax 技术详解

文章目录 1. Filter 过滤器1.1 Filter 概述1.2 Filter 快速入门开发步骤:1.3 Filter 执行流程1.4 Filter 拦截路径配置1.5 过滤器链2. Listener 监听器2.1 Listener 概述2.2 ServletContextListener3. Ajax 技术3.1 Ajax 概述3.2 Ajax 快速入门服务端实现:客户端实现:4. Axi…...