Python | 线程锁 | 3分钟掌握【同步锁】(Threading.Lock)
文章目录
- 概念
- 无锁
- 加锁
- 死锁
- 解决死锁
概念
threading.Lock 同步锁,可以用于保证多个线程对共享数据的独占访问。
当一个线程获取了锁之后,其他线程在此期间将不能再次获取该锁,直到该线程释放锁。这样就可以保证共享数据的独占访问,从而避免数据不一致的问题;但是错误的使用Lock也会发生死锁等待的情况;
无锁
当使用多线程访问并修改公共资源时,若不加锁则会导致数据与预期结果不同;
import threading# 创建一个Lock对象
lock = threading.Lock()# 共享资源
counter = 0# 线程函数
def worker():global counter# 获取锁# 对共享资源进行操作for i in range(1000000):counter += 1print("Counter value: ", counter)# 创建两个线程并启动它们
t1 = threading.Thread(target=worker)
t2 = threading.Thread(target=worker)
t1.start()
t2.start()
t1.join()
t2.join()######################## 结果 ############################
Counter value: 1264715
Counter value: 1326630Process finished with exit code 0
############################################################
究其原因,我们可以假设:
- 当线程t1拿到counter=100时,还没来得及+1;
- 此时线程t2也拿到了counter=100。
- 现在当t1和t2对counter进行加1后,counter值都变成了101并返回给全局变量。
- 我们发现,两个线程在同时使用和修改同一全局变量counter时,总数少加了一个1。
- 因为一个线程在对公共资源做读写的时候,其它线程也能对它进行读写,两个线程拿到了一样的值,这就导致了最后产生的结果数据不一致。
为了防止这个问题,我们对公共资源进行使用时,一定要进行加锁保护;
加锁
import threading# 创建一个Lock对象
lock = threading.Lock()# 共享资源
counter = 0# 线程函数
def worker():global counter# 获取锁lock.acquire()try:# 对共享资源进行操作for i in range(1000000):counter += 1print("Counter value: ", counter)finally:# 释放锁lock.release()# 创建两个线程并启动它们
t1 = threading.Thread(target=worker)
t2 = threading.Thread(target=worker)
t1.start()
t2.start()
t1.join()
t2.join()######################## 结果 #############################
Counter value: 1000000
Counter value: 2000000Process finished with exit code 0
############################################################
因为加了锁,两个线程成功地对计数器分别进行了递增操作。
究其原因,我们可以假设:
- 当线程t1先获取到了锁lock,并对数据处理部分进行了加锁,假设t1拿到counter=1;
- 此时线程t2也想拿counter,但是因为获取不到锁lock,就只能等待lock被释放。
- 当线程t1完全执行完递增操作后,将锁lock给释放掉了;
- 此时t2突然发现它能获取到锁lock了,于是就对t2的数据处理部分进行了加锁,然后执行数据递增逻辑。
- 直到t2的完全执行完递增操作后,将锁lock释放;
- 我们发现,两个线程由于锁的作用,只能顺序性的获取需要的公共资源counter,保证了线程流程执行中公共资源使用的独占性。
因为加锁而且没有出现竞态,所以结果是准确且符合预期;
但同时,在多线程中错误的加锁顺序可能会导致锁等待,也就是俗称的“死锁"现象。
死锁
死锁指的是在多线程或分布式系统中,两个或多个线程或进程因互相等待对方释放资源而陷入无限等待的状态,无法继续执行的情况。
当多个线程或进程互相竞争同一组资源时,如果每个线程都持有一些资源,并且都在等待另一个线程释放它所需要的资源时,就会发生死锁。这种情况下,所有的线程都被阻塞,无法继续执行,从而导致系统出现僵死状态。
import threading# 创建两个Lock对象
lock1 = threading.Lock()
lock2 = threading.Lock()# 线程函数1
def worker1():lock1.acquire()print("Worker 1 acquired lock 1")lock2.acquire()print("Worker 1 acquired lock 2")lock2.release()print("Worker 1 released lock 2")lock1.release()print("Worker 1 released lock 1")# 线程函数2
def worker2():lock2.acquire()print("Worker 2 acquired lock 2")lock1.acquire()print("Worker 2 acquired lock 1")lock1.release()print("Worker 2 released lock 1")lock2.release()print("Worker 2 released lock 2")# 创建两个线程并启动它们
t1 = threading.Thread(target=worker1)
t2 = threading.Thread(target=worker2)
t1.start()
t2.start()
t1.join()
t2.join()######################## 结果 ############################
Worker 1 acquired lock 1
Worker 2 acquired lock 2
...无尽的等待
############################################################
在上面的代码中,我们创建了两个Lock对象,并在两个线程中使用了不同的顺序来获取这两个锁。
线程1首先获取了lock1,然后尝试获取lock2;而线程2则首先获取了lock2,然后尝试获取lock1。
由于两个线程获取锁的顺序不同,它们【可能】会在某个时刻同时持有一个锁,但尝试获取另一个锁时会被阻塞。
例如,当线程1获取了lock1,而线程2获取了lock2时,它们都会等待另一个锁的释放,从而导致死锁。
这时候程序将不再继续执行,而是一直处于等待状态。
这里如果想要要避免死锁,就需要确保不同线程获取锁的顺序是一致的。
也就是说,如果一个线程首先获取了lock1,那么它在尝试获取lock2时也应该按照相同的顺序来获取,而不是反过来。
解决死锁
死锁的产生原因通常是由于多个线程对共享资源的竞争,同时又没有良好的资源分配策略或锁的获取顺序导致的。在设计多线程或分布式系统时,避免死锁是一个重要的问题。常用的避免死锁的方法包括:
- 加锁顺序的规范化
- 资源分配策略的优化
- 使用超时等待等机制。
相关文章:
Python | 线程锁 | 3分钟掌握【同步锁】(Threading.Lock)
文章目录概念无锁加锁死锁解决死锁概念 threading.Lock 同步锁,可以用于保证多个线程对共享数据的独占访问。 当一个线程获取了锁之后,其他线程在此期间将不能再次获取该锁,直到该线程释放锁。这样就可以保证共享数据的独占访问,…...
Linux下安装MySQL8.0的详细步骤(解压tar.xz安装包方式安装)
Linux下安装MySQL8.0的详细步骤 第一步:下载安装配置 第二步:修改密码,并设置远程连接(为了可以在别的机器下面连接该mysql) 第三步:使用Navicat客户端连接 搞了一台云服务器,首先要干的活就是…...
leaflet 绘制多个点的envelope矩形(082)
第082个 点击查看专栏目录 本示例的目的是介绍演示如何在vue+leaflet中如何根据多边形的几个坐标点来绘制envelope矩形。 直接复制下面的 vue+openlayers源代码,操作2分钟即可运行实现效果. 文章目录 示例效果配置方式示例源代码(共78行)安装插件相关API参考:专栏目标示例…...
CAJ论文怎么批量免费转换成Word
大家都知道CAJ文件吗?这是中国学术期刊数据库中的文件,这种文件类型比较特殊。如果想要提取其中的内容使用,该如何操作呢?大家可以试试下面这种免费的caj转word的方法,多个文档也可以一起批量转换。准备材料:CAJ文档、…...
面试必问: 结构体大小的计算方法
结构体大小的计算需同时满足以下几点 一、结构体成员的偏移量必须是当前成员大小的整数倍。(0是任何数的整数倍) 举一个例子 struct Test1{char a; // 当前偏移量为0,是char所占字节数1的整数倍 所以所占大小为1char b; …...
Java中super函数的用法
1 问题 Java中super函数有很多方法,在使用的时候我们应该如何正确区分? 2 方法 三种用法: 访问父类的方法。 调用父类构造方法。 访问父类中的隐藏成员变量。 class A{ int x,y; A(int x,int y){ System.out.println("A"); } } cla…...
第十一届“泰迪杯”数据挖掘挑战赛携“十万”大奖火热来袭
第十一届“泰迪杯”数据挖掘挑战赛 竞赛组织 主办单位: 泰迪杯数据挖掘挑战赛组织委员会 承办单位: 广东泰迪智能科技股份有限公司 人民邮电出版社 协办单位: 重庆市工业与应用数学学会、广东省工业与应用数学学会、广西数学学会、河北省工业…...
分享三个可以在家做的正规兼职工作,看到就是赚到
你可以在家做正式的兼职工作。在线兼职工作值得考虑,时间相对自由。在线兼职收入可能不如线下滴滴和外卖立竿见影,但仍然可以坚持收入。有些人比工作工资发展得更高。当然,天上不会有馅饼,不劳无获。那么有哪些正规的兼职可以在家…...
javaFx实现鼠标穿透画布,同时操作画布和桌面,背景透明,类似ppt批注
一、功能需要由来和大致效果 今天,我们要用javaFx来实现一个鼠标穿透画布的功能,该需求来自于在我们的javaFx桌面应用中,需要实现一个悬浮的桌面侧边工具栏,在工具栏中有画笔绘制,批注的功能,能够实现在任何…...
客户服务知识库的最佳实践7个步骤
每个公司的声誉都依赖于客户,如果客户因为想要购买你的产品找到你,但是了解到你的客户服务做的不好,可能也会放弃你的产品,就像市场营销依赖于潜在客户的关系一样,公司的服务部门也需要依赖于现有客户的关系࿰…...
多重继承的虚函数表
同一个类,不同对象使用同一张虚函数表 不同类使用不同的虚函数表 子类自己添加的虚函数(非重写),在VS中是将此放在第一个继承类的虚函数表里. #include <iostream> using namespace std;class Father { public:virtual void func1() { cout << "Father::f…...
第11篇:Java开发工具使用和代码规范配置
目录 1、IntelliJ IDEA 简介 2. IntelliJ IDEA 下载 3. IntelliJ IDEA 安装和使用 3.1 安装到Windows下 3.2 快速编写 Hello World 程序...
Rust模式匹配
模式匹配 模式匹配是从函数式编程语言(例如:Haskell,Lisp)吸收而来的,用于为复杂的类型系统提供一个轻松的解构能力。rust使用match来提供模式匹配的功能。mathc类似于其它编程语言中的switch-case,但是远…...
GIT:【基础一】必要配置和命令
目录 一、Git安装 二、基础命令 1.git config -l:git配置详细信息 2.git config --system -l:本地git系统自动配置的信息 3.git config --global -l:本地git用户自动配置的信息 4.where git: windows查看git安装目录 5.git各配置…...
黑马程序员-Linux系统编程-01
课程链接 01-Linux命令基础习惯-Linux系统编程_哔哩哔哩_bilibili 课程重点笔记 01-linux命令基础习惯 终端 终端:一切输入、输出的总称,因此终端并不是一定指的是命令行,只要是能进行输入或者输出即可,但是在linux终端上‘’内…...
Python|每日一练|动态规划|图算法|散列表|数组|双指针|单选记录:不同路径|求两个给定正整数的最大公约数和最小公倍数|删除有序数组中的重复项
1、不同路径(数学,动态规划) 一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish”…...
Java常用框架(一)
思维导图 常见知识点 一、SpringBoot 1.简单介绍一下Spring及其优缺点 1.1 概念 重量级企业开发框架EJB的替代品,通过依赖注入、面向切面编程,使用简单Java对象POJO为企业Java开发提供了相对简单的方法。 1.2 优缺点 1.2.1 优点 组件代码轻量级 …...
基于 DSP+FPGA 的高清图像跟踪系统研制
目标识别与跟踪技术是目前图像处理研究的重点方向,在军事和民用领域中 具有广泛的应用价值,如精确制导武器、导弹飞机预警等军事领域,如交通管理、 刑事侦查等民用领域。其中,如何在复杂的背景中,提取、识别与跟踪特定…...
apisix部署
使用k8s部署前打包镜像: FROM centos:7 ARG APISIX_VERSION2.11.0 LABEL apisix_version“${APISIX_VERSION}” RUN yum install -y https://repos.apiseven.com/packages/centos/apache-apisix-repo-1.0-1.noarch.rpm && yum install -y https://repos…...
无聊小知识01.serialVersionUID的作用
什么是serialVersionUIDJava(TM)对象序列化规范中描述到:serialVersionUID用作Serializable类中的版本控件。如果您没有显式声明serialVersionUID,JVM将根据您的Serializable类的各个方面自动为您执行此操作。(http://docs.oracle…...
[ICLR 2022]How Much Can CLIP Benefit Vision-and-Language Tasks?
论文网址:pdf 英文是纯手打的!论文原文的summarizing and paraphrasing。可能会出现难以避免的拼写错误和语法错误,若有发现欢迎评论指正!文章偏向于笔记,谨慎食用 目录 1. 心得 2. 论文逐段精读 2.1. Abstract 2…...
ESP32 I2S音频总线学习笔记(四): INMP441采集音频并实时播放
简介 前面两期文章我们介绍了I2S的读取和写入,一个是通过INMP441麦克风模块采集音频,一个是通过PCM5102A模块播放音频,那如果我们将两者结合起来,将麦克风采集到的音频通过PCM5102A播放,是不是就可以做一个扩音器了呢…...
《基于Apache Flink的流处理》笔记
思维导图 1-3 章 4-7章 8-11 章 参考资料 源码: https://github.com/streaming-with-flink 博客 https://flink.apache.org/bloghttps://www.ververica.com/blog 聚会及会议 https://flink-forward.orghttps://www.meetup.com/topics/apache-flink https://n…...
图表类系列各种样式PPT模版分享
图标图表系列PPT模版,柱状图PPT模版,线状图PPT模版,折线图PPT模版,饼状图PPT模版,雷达图PPT模版,树状图PPT模版 图表类系列各种样式PPT模版分享:图表系列PPT模板https://pan.quark.cn/s/20d40aa…...
RNN避坑指南:从数学推导到LSTM/GRU工业级部署实战流程
本文较长,建议点赞收藏,以免遗失。更多AI大模型应用开发学习视频及资料,尽在聚客AI学院。 本文全面剖析RNN核心原理,深入讲解梯度消失/爆炸问题,并通过LSTM/GRU结构实现解决方案,提供时间序列预测和文本生成…...
3-11单元格区域边界定位(End属性)学习笔记
返回一个Range 对象,只读。该对象代表包含源区域的区域上端下端左端右端的最后一个单元格。等同于按键 End 向上键(End(xlUp))、End向下键(End(xlDown))、End向左键(End(xlToLeft)End向右键(End(xlToRight)) 注意:它移动的位置必须是相连的有内容的单元格…...
莫兰迪高级灰总结计划简约商务通用PPT模版
莫兰迪高级灰总结计划简约商务通用PPT模版,莫兰迪调色板清新简约工作汇报PPT模版,莫兰迪时尚风极简设计PPT模版,大学生毕业论文答辩PPT模版,莫兰迪配色总结计划简约商务通用PPT模版,莫兰迪商务汇报PPT模版,…...
【Veristand】Veristand环境安装教程-Linux RT / Windows
首先声明,此教程是针对Simulink编译模型并导入Veristand中编写的,同时需要注意的是老用户编译可能用的是Veristand Model Framework,那个是历史版本,且NI不会再维护,新版本编译支持为VeriStand Model Generation Suppo…...
若依登录用户名和密码加密
/*** 获取公钥:前端用来密码加密* return*/GetMapping("/getPublicKey")public RSAUtil.RSAKeyPair getPublicKey() {return RSAUtil.rsaKeyPair();}新建RSAUti.Java package com.ruoyi.common.utils;import org.apache.commons.codec.binary.Base64; im…...
Linux-进程间的通信
1、IPC: Inter Process Communication(进程间通信): 由于每个进程在操作系统中有独立的地址空间,它们不能像线程那样直接访问彼此的内存,所以必须通过某种方式进行通信。 常见的 IPC 方式包括&#…...
