浅谈python锁与死锁问题
作者:TechFlow2019 发布时间:2022-06-02 16:38:37
如果你学过操作系统,那么对于锁应该不陌生。锁的含义是线程锁,可以用来指定某一个逻辑或者是资源同一时刻只能有一个线程访问。这个很好理解,就好像是有一个房间被一把锁锁住了,只有拿到钥匙的人才能进入。每一个人从房间门口拿到钥匙进入房间,出房间的时候会把钥匙再放回到门口。这样下一个到门口的人就可以拿到钥匙了。这里的房间就是某一个资源或者是一段逻辑,而拿取钥匙的人其实指的是一个线程。
加锁的原因
我们明白了锁的原理,不禁有了一个问题,我们为什么需要锁呢,它在哪些场景当中会用到呢?
其实它的使用场景非常广,我们举一个非常简单的例子,就是淘宝买东西。我们都知道商家的库存都是有限的,卖掉一个少一个。假如说当前某个商品库存只剩下一个,但当下却有两个人同时购买。两个人同时购买也就是有两个请求同时发起购买请求,如果我们不加锁的话,两个线程同时查询到商品的库存是1,大于0,进行购买逻辑之后,减一。由于两个线程同时执行,所以最后商品的库存会变成-1。
显然商品的库存不应该是一个负数,所以我们需要避免这种情况发生。通过加锁可以完美解决这个问题。我们规定一次只能有一个线程发起购买的请求,那么这样当一个线程将库存减到0的时候,第二个请求就无法修改了,就保证了数据的准确性。
代码实现
那么在Python当中,我们怎么样来实现这个锁呢?
其实很简单,threading库当中已经为我们提供了线程的工具,我们直接拿过来用就可以了。我们通过使用threading当中的Lock对象, 可以很轻易的实现方法加锁的功能。
import threading
class PurchaseRequest:
'''
初始化库存与锁
'''
def __init__(self, initial_value = 0):
self._value = initial_value
self._lock = threading.Lock()
def incr(self,delta=1):
'''
加库存
'''
self._lock.acquire()
self._value += delta
self._lock.release()
def decr(self,delta=1):
'''
减库存
'''
self._lock.acquire()
self._value -= delta
self._lock.release()
我们从代码当中就可以很轻易的看出Lock这个对象的使用方法,我们在进入加锁区(资源抢占区)之前,我们需要先使用lock.acquire()方法获取锁。Lock对象可以保证同一时刻只能有一个线程获取锁,只有获取了锁之后才会继续往下执行。当我们执行完成之后,我们需要把锁“放回门口”,所以需要再调用一下release方法,表示锁的释放。
这里有一个小问题是很多程序员在编程的时候总是会忘记release,导致不必要的bug,而且这种分布式场景当中的bug很难通过测试发现。因为测试的时候往往很难测试并发场景,code review的时候也很容易忽略,因此一旦泄露了还是挺难发现的。
为了解决这个问题,Lock还提供了一种改进的用法,就是使用with语句。with语句我们之前在使用文件的时候用到过,使用with可以替我们完成try catch以及资源回收等工作,我们只管用就完事了。这里也是一样,使用with之后我们就可以不用管锁的申请和释放了,直接写代码就行,所以上面的代码可以改写成这样:
import threading
class PurchaseRequest:
'''
初始化库存与锁
'''
def __init__(self, initial_value = 0):
self._value = initial_value
self._lock = threading.Lock()
def incr(self,delta=1):
'''
加库存
'''
with self._lock:
self._value += delta
def decr(self,delta=1):
'''
减库存
'''
with self._lock:
self._value -= delta
这样看起来是不是清爽很多?
可重入锁
上面介绍的只是最简单的锁,我们经常使用的往往是可重入锁。
什么叫可重入锁呢?简单解释一下,就是在一个线程已经持有了锁的情况下,它可以再次进入被加锁的区域。但是既然线程还持有锁没有释放,那么它不应该还是在加锁区域吗,怎么会有需要再次进入被加锁区域的情况呢?其实是有的,道理也很简单,就是递归。
我们把上面的例子稍微改一点点,就完全不一样了。
import threading
class PurchaseRequest:
'''
初始化库存与锁
'''
def __init__(self, initial_value = 0):
self._value = initial_value
self._lock = threading.Lock()
def incr(self,delta=1):
'''
加库存
'''
with self._lock:
self._value += delta
def decr(self,delta=1):
'''
减库存
'''
with self._lock:
self.incr(-delta)
我们关注一下上面的decr方法,我们用incr来代替了原本的逻辑实现了decr。但是有一个问题是decr也是一个加锁的方法,需要前一个锁释放了才能进入。但它已经持有了锁了,那么这种情况下就会发生死锁。
我们只需要把Lock换成可重入锁就可以解决这个问题,只需要修改一行代码。
import threading
class PurchaseRequest:
'''
初始化库存与锁
我们使用RLock代替了Lock,也可重入锁代替了普通锁
'''
def __init__(self, initial_value = 0):
self._value = initial_value
self._lock = threading.RLock()
def incr(self,delta=1):
'''
加库存
'''
with self._lock:
self._value += delta
def decr(self,delta=1):
'''
减库存
'''
with self._lock:
self.incr(-delta)
总结
今天我们的文章介绍了Python当中锁的使用方法,以及可重入锁的概念。在并发场景下开发和调试都是一个比较困难的工作,稍微不小心就会踩到各种各样的坑,死锁只是其中一种比较常见并且比较容易解决的问题,除此之外还有很多其他各种各样的问题。
针对死锁的问题,Python还提供了其他的解决方案,我们放到下一篇文章当中再和大家分享。
来源:https://www.cnblogs.com/techflow/p/13501104.html
猜你喜欢
- 目录原理先行实践篇完整代码原理先行作为一个资深的小说爱好者,国内很多小说网站如出一辙,什么 🖊*阁啊等等,大都是 get 请求返回 html
- 具体代码如下所示:#字符串反转def reverse (s): rt = '' for i in r
- pip用pipdeptree查看包依赖1、安装pipdeptreepip install pipdeptree2、使用pipdeptree查
- SQL Server有两种备份方式,一种是使用BACKUP DATABASE将数据库文件备份出去,另外一种就是直接拷贝数据库文件mdf和日志
- k8s 的调度器 kube-schedulerkube-scheduler 作为 k8s 的调度器,就好比人的大脑,将行动指定传递
- 指令和程序计算机的硬件系统通常由五大部件构成,包括:运算器、控制器、存储器、输入设备和输出设备。其中,运算器和控制器放在一起就是我们通常所说
- 1、拆箱>>> a, b, c = 1, 2, 3>>> a, b, c(1, 2, 3)>>
- SQL Server中的集合运算包括UNION(合并),EXCEPT(差集)和INTERSECT(相交)三种。集合运算的基本使用1.UNIO
- 在学习Python3的正则表达式的时候遇到一个例子#!/usr/bin/python3import reline = "Cats
- 本文实例为大家分享了Vue实现web分页组件的具体代码,供大家参考,具体内容如下效果演示源代码<!DOCTYPE html>&l
- 输出用print加上字符串,就可以向屏幕上输出指定的文字。比如输出'hello, world',用代码实现如下:>&g
- 事情是这样的,笔者在使用docx库的时候遇到了文字的缩进问题,具体需求是:正文内容每段首行顶两格,也就是向右缩进两个字符,像这样:需求看似很
- 使用python进行程序编写时,经常会使用第三方模块包。这种包我们可以通过python setup install 进行安装后,通过impo
- 前言一个非常神秘的魔术方法。这个方法非常不起眼,用途狭窄,我几乎从未注意过它,然而,当发现它可能是上述“定律”的唯一例外情况时,我认为值得再
- 前言今天我看了一下自己的文件夹,发现了自己写了许多似乎很无聊的代码。于是乎,一个想法油然而生:“生活已经很无聊了,不如再无聊一点叭”。说干就
- KNN核心算法函数,具体内容如下#! /usr/bin/env python3# -*- coding: utf-8 -*-# fileNa
- 本人自己写过三个图片轮播,一个是简单的原生JS实现的,没有什么动画效果的,一个是结合JQuery实现的,淡入淡出切换的。现在想做一个酷一点的
- 代码片段是开发者每天都要面对的东西,甚至有时候查找代码片段的时间比编写新代码的时间还要多。因为如果找到能够在项目中直接使用的代码片段,这意味
- 先制作一些数据:import numpy as npimport tensorflow as tfimport matplotlib.pyp
- Dean Edwards 最近有篇文章很精彩,忍不住在这里翻译下。-- Split --很多 Javascript 框架都提供了自定义事件(