资讯专栏INFORMATION COLUMN

python并发场景锁的使用方法

89542767 / 200人阅读

  小编写这篇文章的一个主要目的,主要是来给大家介绍关于python的一些事情,python的使用场景是比较的多的,主要涉及到其中的一些方方面面,那么,它的并发场景使用方法是什么呢?下面就给大家详细解答下。


  前言


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


  加锁的原因


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


  其实它的使用场景非常广,我们举一个非常简单的例子,就是淘宝买东西。我们都知道商家的库存都是有限的,卖掉一个少一个。假如说当前某个商品库存只剩下一个,但当下却有两个人同时购买。两个人同时购买也就是有两个请求同时发起购买请求,如果我们不加锁的话,两个线程同时查询到商品的库存是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当中锁的使用方法,以及可重入锁的概念。在并发场景下开发和调试都是一个比较困难的工作,稍微不小心就会踩到各种各样的坑,死锁只是其中一种比较常见并且比较容易解决的问题,除此之外还有很多其他各种各样的问题。


  综上所述,这篇文章就给大家介绍到这里了,希望可以给大家带来帮助。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/128418.html

相关文章

  • 基于锁的并发算法 vs 无锁的并发算法

    摘要:测试吞吐量的时候,是通过每一种实现都重复测试超过次,每一次都运行秒以上,以保证系统足够预热,下面的结果都是第次之后平均每秒吞吐量。以我的经验看,教学和开发中的无锁算法,不仅能显著改善吞吐量同时他们也提供更低的延迟。 上周在由Heinz Kabutz通过JCrete 组织的开放空间会议(unconference)上,我参加一个新的java规范 JSR166 StampedLock 的审查...

    tomorrowwu 评论0 收藏0
  • python并发4:使用thread处理并发

    摘要:如果某线程并未使用很多操作,它会在自己的时间片内一直占用处理器和。在中使用线程在和等大多数类系统上运行时,支持多线程编程。守护线程另一个避免使用模块的原因是,它不支持守护线程。 这一篇是Python并发的第四篇,主要介绍进程和线程的定义,Python线程和全局解释器锁以及Python如何使用thread模块处理并发 引言&动机 考虑一下这个场景,我们有10000条数据需要处理,处理每条...

    joywek 评论0 收藏0
  • Java中的锁以及sychronized实现机制

    摘要:有可能,会造成优先级反转或者饥饿现象。悲观锁在中的使用,就是利用各种锁。对于而言,其是独享锁。偏向锁,顾名思义,它会偏向于第一个访问锁的线程,大多数情况下锁不仅不存在多线程竞争,而且总是由同一线程多次获得。 理解锁的基础知识 如果想要透彻的理解java锁的来龙去脉,需要先了解以下基础知识。 基础知识之一:锁的类型 按照其性质分类 公平锁/非公平锁 公平锁是指多个线程按照申请锁的顺序来获...

    linkin 评论0 收藏0
  • PyTips 0x 12 - Python 线程与协程(1)

    摘要:中关于线程的标准库是,之前在版本中的在之后更名为,无论是还是都应该尽量避免使用较为底层的而应该使用。而与线程相比,协程尤其是结合事件循环无论在编程模型还是语法上,看起来都是非常友好的单线程同步过程。 项目地址:https://git.io/pytips 要说到线程(Thread)与协程(Coroutine)似乎总是需要从并行(Parallelism)与并发(Concurrency)谈起...

    el09xccxy 评论0 收藏0
  • [Java并发-10] ReadWriteLock:快速实现一个完备的缓存

    摘要:此时线程和会再有一个线程能够获取写锁,假设是,如果不采用再次验证的方式,此时会再次查询数据库。而实际上线程已经把缓存的值设置好了,完全没有必要再次查询数据库。 大家知道了Java中使用管程同步原语,理论上可以解决所有的并发问题。那 Java SDK 并发包里为什么还有很多其他的工具类呢?原因很简单:分场景优化性能,提升易用性 今天我们就介绍一种非常普遍的并发场景:读多写少场景。实际工作...

    nevermind 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<