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

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
############################################################

究其原因,我们可以假设:

  1. 当线程t1拿到counter=100时,还没来得及+1;
  2. 此时线程t2也拿到了counter=100。
  3. 现在当t1和t2对counter进行加1后,counter值都变成了101并返回给全局变量。
  4. 我们发现,两个线程在同时使用和修改同一全局变量counter时,总数少加了一个1。
  5. 因为一个线程在对公共资源做读写的时候,其它线程也能对它进行读写,两个线程拿到了一样的值,这就导致了最后产生的结果数据不一致。

为了防止这个问题,我们对公共资源进行使用时,一定要进行加锁保护;

加锁

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
############################################################

因为加了锁,两个线程成功地对计数器分别进行了递增操作。

究其原因,我们可以假设:

  1. 当线程t1先获取到了锁lock,并对数据处理部分进行了加锁,假设t1拿到counter=1;
  2. 此时线程t2也想拿counter,但是因为获取不到锁lock,就只能等待lock被释放。
  3. 当线程t1完全执行完递增操作后,将锁lock给释放掉了;
  4. 此时t2突然发现它能获取到锁lock了,于是就对t2的数据处理部分进行了加锁,然后执行数据递增逻辑。
  5. 直到t2的完全执行完递增操作后,将锁lock释放;
  6. 我们发现,两个线程由于锁的作用,只能顺序性的获取需要的公共资源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时也应该按照相同的顺序来获取,而不是反过来。


解决死锁

死锁的产生原因通常是由于多个线程对共享资源的竞争,同时又没有良好的资源分配策略或锁的获取顺序导致的。在设计多线程或分布式系统时,避免死锁是一个重要的问题。常用的避免死锁的方法包括:

  1. 加锁顺序的规范化
  2. 资源分配策略的优化
  3. 使用超时等待等机制。

相关文章:

Python | 线程锁 | 3分钟掌握【同步锁】(Threading.Lock)

文章目录概念无锁加锁死锁解决死锁概念 threading.Lock 同步锁,可以用于保证多个线程对共享数据的独占访问。 当一个线程获取了锁之后,其他线程在此期间将不能再次获取该锁,直到该线程释放锁。这样就可以保证共享数据的独占访问&#xff0c…...

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个步骤

每个公司的声誉都依赖于客户,如果客户因为想要购买你的产品找到你,但是了解到你的客户服务做的不好,可能也会放弃你的产品,就像市场营销依赖于潜在客户的关系一样,公司的服务部门也需要依赖于现有客户的关系&#xff0…...

多重继承的虚函数表

同一个类,不同对象使用同一张虚函数表 不同类使用不同的虚函数表 子类自己添加的虚函数(非重写),在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模式匹配

模式匹配 模式匹配是从函数式编程语言&#xff08;例如&#xff1a;Haskell&#xff0c;Lisp&#xff09;吸收而来的&#xff0c;用于为复杂的类型系统提供一个轻松的解构能力。rust使用match来提供模式匹配的功能。mathc类似于其它编程语言中的switch-case&#xff0c;但是远…...

GIT:【基础一】必要配置和命令

目录 一、Git安装 二、基础命令 1.git config -l&#xff1a;git配置详细信息 2.git config --system -l&#xff1a;本地git系统自动配置的信息 3.git config --global -l&#xff1a;本地git用户自动配置的信息 4.where git&#xff1a; windows查看git安装目录 5.git各配置…...

黑马程序员-Linux系统编程-01

课程链接 01-Linux命令基础习惯-Linux系统编程_哔哩哔哩_bilibili 课程重点笔记 01-linux命令基础习惯 终端 终端&#xff1a;一切输入、输出的总称&#xff0c;因此终端并不是一定指的是命令行&#xff0c;只要是能进行输入或者输出即可&#xff0c;但是在linux终端上‘’内…...

Python|每日一练|动态规划|图算法|散列表|数组|双指针|单选记录:不同路径|求两个给定正整数的最大公约数和最小公倍数|删除有序数组中的重复项

1、不同路径&#xff08;数学&#xff0c;动态规划&#xff09; 一个机器人位于一个 m x n 网格的左上角 &#xff08;起始点在下图中标记为 “Start” &#xff09;。 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角&#xff08;在下图中标记为 “Finish”…...

Java常用框架(一)

思维导图 常见知识点 一、SpringBoot 1.简单介绍一下Spring及其优缺点 1.1 概念 重量级企业开发框架EJB的替代品&#xff0c;通过依赖注入、面向切面编程&#xff0c;使用简单Java对象POJO为企业Java开发提供了相对简单的方法。 1.2 优缺点 1.2.1 优点 组件代码轻量级 …...

基于 DSP+FPGA 的高清图像跟踪系统研制

目标识别与跟踪技术是目前图像处理研究的重点方向&#xff0c;在军事和民用领域中 具有广泛的应用价值&#xff0c;如精确制导武器、导弹飞机预警等军事领域&#xff0c;如交通管理、 刑事侦查等民用领域。其中&#xff0c;如何在复杂的背景中&#xff0c;提取、识别与跟踪特定…...

apisix部署

使用k8s部署前打包镜像&#xff1a; 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&#xff08;TM&#xff09;对象序列化规范中描述到&#xff1a;serialVersionUID用作Serializable类中的版本控件。如果您没有显式声明serialVersionUID&#xff0c;JVM将根据您的Serializable类的各个方面自动为您执行此操作。(http://docs.oracle…...

地震勘探——干扰波识别、井中地震时距曲线特点

目录 干扰波识别反射波地震勘探的干扰波 井中地震时距曲线特点 干扰波识别 有效波&#xff1a;可以用来解决所提出的地质任务的波&#xff1b;干扰波&#xff1a;所有妨碍辨认、追踪有效波的其他波。 地震勘探中&#xff0c;有效波和干扰波是相对的。例如&#xff0c;在反射波…...

ES6从入门到精通:前言

ES6简介 ES6&#xff08;ECMAScript 2015&#xff09;是JavaScript语言的重大更新&#xff0c;引入了许多新特性&#xff0c;包括语法糖、新数据类型、模块化支持等&#xff0c;显著提升了开发效率和代码可维护性。 核心知识点概览 变量声明 let 和 const 取代 var&#xf…...

连锁超市冷库节能解决方案:如何实现超市降本增效

在连锁超市冷库运营中&#xff0c;高能耗、设备损耗快、人工管理低效等问题长期困扰企业。御控冷库节能解决方案通过智能控制化霜、按需化霜、实时监控、故障诊断、自动预警、远程控制开关六大核心技术&#xff0c;实现年省电费15%-60%&#xff0c;且不改动原有装备、安装快捷、…...

Golang dig框架与GraphQL的完美结合

将 Go 的 Dig 依赖注入框架与 GraphQL 结合使用&#xff0c;可以显著提升应用程序的可维护性、可测试性以及灵活性。 Dig 是一个强大的依赖注入容器&#xff0c;能够帮助开发者更好地管理复杂的依赖关系&#xff0c;而 GraphQL 则是一种用于 API 的查询语言&#xff0c;能够提…...

React19源码系列之 事件插件系统

事件类别 事件类型 定义 文档 Event Event 接口表示在 EventTarget 上出现的事件。 Event - Web API | MDN UIEvent UIEvent 接口表示简单的用户界面事件。 UIEvent - Web API | MDN KeyboardEvent KeyboardEvent 对象描述了用户与键盘的交互。 KeyboardEvent - Web…...

Rust 异步编程

Rust 异步编程 引言 Rust 是一种系统编程语言,以其高性能、安全性以及零成本抽象而著称。在多核处理器成为主流的今天,异步编程成为了一种提高应用性能、优化资源利用的有效手段。本文将深入探讨 Rust 异步编程的核心概念、常用库以及最佳实践。 异步编程基础 什么是异步…...

【Redis】笔记|第8节|大厂高并发缓存架构实战与优化

缓存架构 代码结构 代码详情 功能点&#xff1a; 多级缓存&#xff0c;先查本地缓存&#xff0c;再查Redis&#xff0c;最后才查数据库热点数据重建逻辑使用分布式锁&#xff0c;二次查询更新缓存采用读写锁提升性能采用Redis的发布订阅机制通知所有实例更新本地缓存适用读多…...

RabbitMQ入门4.1.0版本(基于java、SpringBoot操作)

RabbitMQ 一、RabbitMQ概述 RabbitMQ RabbitMQ最初由LShift和CohesiveFT于2007年开发&#xff0c;后来由Pivotal Software Inc.&#xff08;现为VMware子公司&#xff09;接管。RabbitMQ 是一个开源的消息代理和队列服务器&#xff0c;用 Erlang 语言编写。广泛应用于各种分布…...

在鸿蒙HarmonyOS 5中使用DevEco Studio实现企业微信功能

1. 开发环境准备 ​​安装DevEco Studio 3.1​​&#xff1a; 从华为开发者官网下载最新版DevEco Studio安装HarmonyOS 5.0 SDK ​​项目配置​​&#xff1a; // module.json5 {"module": {"requestPermissions": [{"name": "ohos.permis…...

LOOI机器人的技术实现解析:从手势识别到边缘检测

LOOI机器人作为一款创新的AI硬件产品&#xff0c;通过将智能手机转变为具有情感交互能力的桌面机器人&#xff0c;展示了前沿AI技术与传统硬件设计的完美结合。作为AI与玩具领域的专家&#xff0c;我将全面解析LOOI的技术实现架构&#xff0c;特别是其手势识别、物体识别和环境…...