今天是 Python专题 的第24篇文章,我们一起来聊聊多线程场景当中不可或缺的另外一个部分——  。

很多人学习python,不知道从何学起。
很多人学习python,掌握了基本语法过后,不知道在哪里寻找案例上手。
很多已经做案例的人,却不知道如何去学习更加高深的知识。
那么针对这三类人,我给大家提供一个好的学习平台,免费领取视频教程,电子书籍,以及课程的源代码!
QQ群:101677771

如果你学过操作系统,那么对于锁应该不陌生。锁的含义是线程锁,可以用来指定某一个逻辑或者是资源 同一时刻只能有一个线程访问 。这个很好理解,就好像是有一个房间被一把锁锁住了,只有拿到钥匙的人才能进入。每一个人从房间门口拿到钥匙进入房间,出房间的时候会把钥匙再放回到门口。这样下一个到门口的人就可以拿到钥匙了。这里的房间就是某一个资源或者是一段逻辑,而拿取钥匙的人其实指的是一个线程。

加锁的原因

我们明白了锁的原理,不禁有了一个问题,我们为什么需要锁呢,它在哪些场景当中会用到呢?

其实它的使用场景非常广,我们举一个非常简单的例子,就是淘宝买东西。我们都知道商家的库存都是有限的,卖掉一个少一个。假如说当前某个商品库存只剩下一个,但当下却有两个人同时购买。两个人同时购买也就是有两个请求同时发起购买请求,如果我们不加锁的话,两个线程同时查询到商品的库存是1,大于0,进行购买逻辑之后,减一。由于两个线程同时执行,所以最后商品的库存会变成-1。

显然商品的库存不应该是一个负数,所以我们需要避免这种情况发生。通过 加锁 可以完美解决这个问题。我们规定一次只能有一个线程发起购买的请求,那么这样当一个线程将库存减到0的时候,第二个请求就无法修改了,就保证了数据的准确性。

代码实现

那么在Python当中,我们怎么样来实现这个锁呢?

其实很简单,threading库当中已经为我们提供了线程的工具,我们直接拿过来用就可以了。我们通过使用threading当中的Lock对象, 可以很轻易的实现方法加锁的功能。

import threadingclass PurchaseRequest:'''初始化库存与锁'''def __init__(self, initial_value = 0):self._value = initial_valueself._lock = threading.Lock()def incr(self,delta=1):'''加库存'''self._lock.acquire()self._value += deltaself._lock.release()def decr(self,delta=1):'''减库存'''self._lock.acquire()self._value -= deltaself._lock.release()

我们从代码当中就可以很轻易的看出Lock这个对象的使用方法,我们在进入 加锁区 (资源抢占区)之前,我们需要先使用lock.acquire()方法获取锁。Lock对象可以保证同一时刻只能有一个线程获取锁,只有获取了锁之后才会继续往下执行。当我们执行完成之后,我们需要把锁“放回门口”,所以需要再调用一下release方法,表示锁的释放。

这里有一个小问题是很多程序员在编程的时候总是会 忘记release,导致不必要的bug ,而且这种分布式场景当中的bug很难通过测试发现。因为测试的时候往往很难测试并发场景,code review的时候也很容易忽略,因此一旦泄露了还是挺难发现的。

为了解决这个问题,Lock还提供了一种改进的用法,就是 使用with语句 。with语句我们之前在使用文件的时候用到过,使用with可以替我们完成try catch以及资源回收等工作,我们只管用就完事了。这里也是一样,使用with之后我们就可以不用管锁的申请和释放了,直接写代码就行,所以上面的代码可以改写成这样:

import threadingclass PurchaseRequest:'''初始化库存与锁'''def __init__(self, initial_value = 0):self._value = initial_valueself._lock = threading.Lock()def incr(self,delta=1):'''加库存'''with self._lock:self._value += deltadef decr(self,delta=1):'''减库存'''with self._lock:self._value -= delta

这样看起来是不是清爽很多?

可重入锁

上面介绍的只是最简单的锁,我们经常使用的往往是 可重入锁 。

什么叫可重入锁呢?简单解释一下,就是在一个线程已经持有了锁的情况下,它可以再次进入被加锁的区域。但是既然线程还持有锁没有释放,那么它不应该还是在加锁区域吗,怎么会有需要再次进入被加锁区域的情况呢?其实是有的, 道理也很简单,就是递归 。

我们把上面的例子稍微改一点点,就完全不一样了。

import threadingclass PurchaseRequest:'''初始化库存与锁'''def __init__(self, initial_value = 0):self._value = initial_valueself._lock = threading.Lock()def incr(self,delta=1):'''加库存'''with self._lock:self._value += deltadef decr(self,delta=1):'''减库存'''with self._lock:self.incr(-delta)

我们关注一下上面的decr方法,我们用incr来代替了原本的逻辑实现了decr。但是有一个问题是decr也是一个加锁的方法,需要前一个锁释放了才能进入。但它已经持有了锁了,那么这种情况下就会发生 死锁 。

我们只需要把Lock换成可重入锁就可以解决这个问题,只需要修改一行代码。

import threadingclass PurchaseRequest:'''初始化库存与锁我们使用RLock代替了Lock,也可重入锁代替了普通锁'''def __init__(self, initial_value = 0):self._value = initial_valueself._lock = threading.RLock()def incr(self,delta=1):'''加库存'''with self._lock:self._value += deltadef decr(self,delta=1):'''减库存'''with self._lock:self.incr(-delta)

今天我们的文章介绍了Python当中锁的使用方法,以及可重入锁的概念。在并发场景下开发和调试都是一个比较困难的工作,稍微不小心就会踩到各种各样的坑, 死锁只是其中一种比较常见并且比较容易解决的问题 ,除此之外还有很多其他各种各样的问题。

Python | 浅谈并发锁与死锁问题相关推荐

  1. 搞懂分布式技术16:浅谈分布式锁的几种方案

    搞懂分布式技术16:浅谈分布式锁的几种方案 前言 随着互联网技术的不断发展,数据量的不断增加,业务逻辑日趋复杂,在这种背景下,传统的集中式系统已经无法满足我们的业务需求,分布式系统被应用在更多的场景, ...

  2. [转]浅谈MS-SQL锁机制

    本文转自:http://study.99net.net/study/database/mssql/1085625420.html 浅谈MS-SQL锁机制 2004-05-27     锁的概述 一. ...

  3. 浅谈并发与并行(一)

    http://www.cnblogs.com/yangecnu/p/3164167.html 浅谈并发与并行(一)

  4. 【python 笔记/小白快速入门python】python浅谈(一)犹抱琵琶半遮面

    python浅谈(一)犹抱琵琶半遮面 继浅谈(零)初识庐山真面目[https://blog.csdn.net/HarryOtter/article/details/90519877 ] 之后,终于窥得 ...

  5. 从“惊群”的现象来看并发锁,“死锁”问题的解决方案丨Redis单线程|共享内存|无锁实现|原子操作CAS

    从"惊群"的现象来看并发锁,"死锁"问题的解决方案 视频讲解如下,点击观看: 从"惊群"的现象来看并发锁,"死锁"问题的 ...

  6. 浅谈Java锁,与JUC的常用类,集合安全类,常用辅助类,读写锁,阻塞队列,线程池,ForkJoin,volatile,单例模式不安全,CAS,各种锁

    浅谈JUC的常用类 JUC就是java.util.concurrent-包下的类 回顾多线程 Java默认有几个线程? 2 个 mian.GC Java 真的可以开启线程吗? 开不了,点击源码得知:本 ...

  7. 你用对锁了吗?浅谈 Java “锁” 事

    每个时代,都不会亏待会学习的人 大家好,我是yes. 本来打算继续写消息队列的东西的,但是最近在带新同事,发现新同事对于锁这方面有一些误解,所以今天就来谈谈"锁"事和 Java 中 ...

  8. java 单例 读写锁_你用对锁了吗?浅谈 Java “锁” 事

    每个时代,都不会亏待会学习的人 大家好,我是yes. 本来打算继续写消息队列的东西的,但是最近在带新同事,发现新同事对于锁这方面有一些误解,所以今天就来谈谈"锁"事和 Java 中 ...

  9. 【转】浅谈MS-SQL锁机制

    锁的概述 一. 为什么要引入锁 多个用户同时对数据库的并发操作时会带来以下数据不一致的问题: 丢失更新 A,B两个用户读同一数据并进行修改,其中一个用户的修改结果破坏了另一个修改的结果,比如订票系统 ...

最新文章

  1. HOWTO:InstallShield中如何制作应用程序的卸载快捷方式
  2. 【数理知识】《随机过程》方兆本老师-第3章-Markov 过程
  3. 5.SEH(结构化异常处理)
  4. 如何使用Python制作一个会动的地球仪?
  5. Java黑皮书课后题第9章:*9.11(代数:2*2的线性方程)为一个2*2的线性方程设计一个名为LinearEquation的类
  6. Java transient关键字(序列化避免被反序列化获取敏感信息)
  7. 机器学习入门系列:关于机器学习算法你需要了解的东西、如何开发机器学习模型?...
  8. 使用npm安装vue项目+使用
  9. linux查看内存条pn,查看电脑内存条型号的两种方法【图文】
  10. 在线文本比较工具-toolfk程序员在线工具网
  11. 解决git未指定冲突处理方法的问题 - hint: Pulling without specifying how to reconcile divergent branches ishint: di
  12. 安卓WebView 屏蔽所有类型JS弹窗
  13. 360全景倒车影像怎么看_最近淘了一个360度全景倒车影像-4路行车记录仪监控录像,和大家分享一下...
  14. C# 身份证号码验证正则和验证函数
  15. pytorch torch.nn到底是什么?
  16. win7资源管理器总是崩溃
  17. (产品求职)阿里巴巴价值观和业务图
  18. 72名图灵奖获得者的成就
  19. python线性回归实例 x轴坐标相同_python深度学习-tensorflow实现一个线性回归的案例...
  20. Excel导入导出详细教程------EasyExcel功能整合

热门文章

  1. python-format格式化专题介绍1909
  2. mysql-外键操作-级联删除
  3. javascript-常用内置对象-随堂
  4. prometheus修改数据保留时间
  5. 关于Java的反射机制,你需要理解这些...
  6. Redis 高可用性实践
  7. Linux磁盘管理2
  8. 面试官系统精讲Java源码及大厂真题 - 09 TreeMap 和 LinkedHashMap 核心源码解析
  9. Nginx的11个phases
  10. 容器编排技术 -- Kubernetes kubectl set 命令详解