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…...
Java如何权衡是使用无序的数组还是有序的数组
在 Java 中,选择有序数组还是无序数组取决于具体场景的性能需求与操作特点。以下是关键权衡因素及决策指南: ⚖️ 核心权衡维度 维度有序数组无序数组查询性能二分查找 O(log n) ✅线性扫描 O(n) ❌插入/删除需移位维护顺序 O(n) ❌直接操作尾部 O(1) ✅内存开销与无序数组相…...
Neo4j 集群管理:原理、技术与最佳实践深度解析
Neo4j 的集群技术是其企业级高可用性、可扩展性和容错能力的核心。通过深入分析官方文档,本文将系统阐述其集群管理的核心原理、关键技术、实用技巧和行业最佳实践。 Neo4j 的 Causal Clustering 架构提供了一个强大而灵活的基石,用于构建高可用、可扩展且一致的图数据库服务…...
IT供电系统绝缘监测及故障定位解决方案
随着新能源的快速发展,光伏电站、储能系统及充电设备已广泛应用于现代能源网络。在光伏领域,IT供电系统凭借其持续供电性好、安全性高等优势成为光伏首选,但在长期运行中,例如老化、潮湿、隐裂、机械损伤等问题会影响光伏板绝缘层…...
Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决
Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决 问题背景 在一个基于 Spring Cloud Gateway WebFlux 构建的微服务项目中,新增了一个本地验证码接口 /code,使用函数式路由(RouterFunction)和 Hutool 的 Circle…...
Java线上CPU飙高问题排查全指南
一、引言 在Java应用的线上运行环境中,CPU飙高是一个常见且棘手的性能问题。当系统出现CPU飙高时,通常会导致应用响应缓慢,甚至服务不可用,严重影响用户体验和业务运行。因此,掌握一套科学有效的CPU飙高问题排查方法&…...
C++课设:简易日历程序(支持传统节假日 + 二十四节气 + 个人纪念日管理)
名人说:路漫漫其修远兮,吾将上下而求索。—— 屈原《离骚》 创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊) 专栏介绍:《编程项目实战》 目录 一、为什么要开发一个日历程序?1. 深入理解时间算法2. 练习面向对象设计3. 学习数据结构应用二、核心算法深度解析…...
Git 3天2K星标:Datawhale 的 Happy-LLM 项目介绍(附教程)
引言 在人工智能飞速发展的今天,大语言模型(Large Language Models, LLMs)已成为技术领域的焦点。从智能写作到代码生成,LLM 的应用场景不断扩展,深刻改变了我们的工作和生活方式。然而,理解这些模型的内部…...
【Android】Android 开发 ADB 常用指令
查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...
STM32---外部32.768K晶振(LSE)无法起振问题
晶振是否起振主要就检查两个1、晶振与MCU是否兼容;2、晶振的负载电容是否匹配 目录 一、判断晶振与MCU是否兼容 二、判断负载电容是否匹配 1. 晶振负载电容(CL)与匹配电容(CL1、CL2)的关系 2. 如何选择 CL1 和 CL…...
Linux系统部署KES
1、安装准备 1.版本说明V008R006C009B0014 V008:是version产品的大版本。 R006:是release产品特性版本。 C009:是通用版 B0014:是build开发过程中的构建版本2.硬件要求 #安全版和企业版 内存:1GB 以上 硬盘…...
