一、关于死锁。

死锁,就是当多个进程或者线程在执行的过程中,因争夺共享资源而造成的一种互相等待的现象,一旦产生了死锁,不加人工处理,程序会一直等待下去,这也被称为死锁进程。

下面是一个产生“死锁”现象的例子:

import threading

import time

lock_a = threading.Lock()

lock_b = threading.Lock()

class test_thread(threading.Thread):

def __init__(self):

super(test_thread,self).__init__()

def run(self):

self.fun1()

self.fun2()

def fun1(self):

lock_a.acquire()

print "I am %s , get res: %s---%s" %(self.name, "ResA",time.time())

lock_b.acquire()

print "I am %s , get res: %s---%s" %(self.name, "ResB",time.time())

lock_b.release()

lock_a.release()

def fun2(self):

lock_b.acquire()

print ("I am %s , get res: %s---%s" %(self.name, "ResB",time.time()))

time.sleep(0.2)

lock_a.acquire()

print ("I am %s , get res: %s---%s" %(self.name, "ResA",time.time()))

lock_a.release()

lock_b.release()

if __name__ == "__main__":

print "start---------------------------%s"%(time.time())

for i in range(0, 10):

my_thread = test_thread()

my_thread.start()

输出执行结果:

start---------------------------1494682814.1

I am Thread-1 , get res: ResA---1494682814.1

I am Thread-1 , get res: ResB---1494682814.1

I am Thread-1 , get res: ResB---1494682814.1

I am Thread-2 , get res: ResA---1494682814.1

下面来分析代码,为什么会产生死锁:

开了10个线程,首先肯定会有一个线程去拿到lock_a这把锁,其余的线程只能阻塞,一直等到lock_a这把锁被释放,然后这个线程又获得了一把锁就是lock_b,执行完了一条print操作后,释放lock_b这把锁,此时,其他的线程还是没有办法去执行func1里面的资源,释放了lock_b这把锁后,紧接着lock_a也被释放了,此时,下一个线程就可以去执行func1中的资源了,接下来,线程1执行到了func2拿到了lock_b这把锁,然后执行一个print输出,I am Thread-1 , get res: ResB---1494682814.1,sleep0.2秒,在第一个线程sleep的过程中第二个线程开始执行,第二个线程在执行func1的时候,首先拿到了lock_a这把锁,执行了下面的print语句,I am Thread-2 , get res: ResA---1494682814.1,此时的情况就是线程1拿到了lock_b这把锁,线程2拿到了lock_a这把锁,那么问题来了,线程2如果想继续执行func1后面的内容,需要去获得lock_b这把锁,而此时lock_b锁已经被线程1拿走了(线程1执行到了func2拿走了lock_b锁,并且还没被释放~),线程1,sleep0.2秒后,需要继续执行func2中的内容,此时,需要拿到lock_a锁,(lock_a锁,被线程2执行func1的时候拿走了,并且没有被释放~),现在的情况就是线程1需要lock_a锁,但是lock_a在线程2手里,线程2需要lock_b锁,但是lock_b锁在线程1手里~双方都没有办法执行到后面的释放操作。

这也就相当于两个人要做交易,甲手里有苹果乙手里有菠萝,甲想吃乙手里的菠萝,乙想吃甲手里的苹果,但是谁都不愿意先把自己手里的东西先给对方~所以程序就会一直卡在这。

这就是死锁形成的原理。

二、递归锁。

解决死锁问题有一个特别有效的方法,就是递归锁.

递归锁与普通的互斥锁最大的不同就是,一个锁的对象内部,维护了一个计数器,这个计数器的初始值是0,当一个线程acquire一次这个锁时,内部计数器+1,但是,这把锁的计数器一旦大于0,其他的线程是无法拿到这把锁的,只有当前线程可以拿。

(当前线程acquire一次,计数器+1,release一次计数器-1,所以,当前的线程想要彻底释放掉递归锁,acquire多少次,就要release多少次!!!)

(这个计数器,就是递归锁中的count。)

还拿刚才的那段产生死锁的代码来举例:

import threading

import time

r_lock = threading.RLock() #RLock是用来产生递归锁的一个类,产生一个递归锁。

class test_thread(threading.Thread):

def __init__(self):

super(test_thread,self).__init__()

def run(self):

self.fun1()

self.fun2()

def fun1(self):

r_lock.acquire() #count+1

print "I am %s , get res: %s---%s" %(self.name, "ResA",time.time())

r_lock.acquire() #count再+1

print "I am %s , get res: %s---%s" %(self.name, "ResB",time.time())

r_lock.release() #count -1

r_lock.release() #count 再-1

def fun2(self):

r_lock.acquire()

print ("I am %s , get res: %s---%s" %(self.name, "ResB",time.time()))

time.sleep(0.2)

r_lock.acquire()

print ("I am %s , get res: %s---%s" %(self.name, "ResA",time.time()))

r_lock.release()

r_lock.release()

if __name__ == "__main__":

print "start---------------------------%s"%(time.time())

r_lock = threading.RLock()

for i in range(0, 10):

my_thread = test_thread()

my_thread.start()

输出结果:

start---------------------------1494687542.43

I am Thread-1 , get res: ResA---1494687542.43

I am Thread-1 , get res: ResB---1494687542.43

I am Thread-1 , get res: ResB---1494687542.43

I am Thread-1 , get res: ResA---1494687542.63

I am Thread-2 , get res: ResA---1494687542.63

I am Thread-2 , get res: ResB---1494687542.63

I am Thread-2 , get res: ResB---1494687542.63

I am Thread-2 , get res: ResA---1494687542.83

I am Thread-4 , get res: ResA---1494687542.83

I am Thread-4 , get res: ResB---1494687542.83

I am Thread-4 , get res: ResB---1494687542.83

I am Thread-4 , get res: ResA---1494687543.04

I am Thread-6 , get res: ResA---1494687543.04

I am Thread-6 , get res: ResB---1494687543.04

I am Thread-6 , get res: ResB---1494687543.04

I am Thread-6 , get res: ResA---1494687543.24

I am Thread-8 , get res: ResA---1494687543.24

I am Thread-8 , get res: ResB---1494687543.24

I am Thread-8 , get res: ResB---1494687543.24

I am Thread-8 , get res: ResA---1494687543.44

I am Thread-10 , get res: ResA---1494687543.44

I am Thread-10 , get res: ResB---1494687543.44

I am Thread-10 , get res: ResB---1494687543.44

I am Thread-10 , get res: ResA---1494687543.65

I am Thread-5 , get res: ResA---1494687543.65

I am Thread-5 , get res: ResB---1494687543.65

I am Thread-5 , get res: ResB---1494687543.65

I am Thread-5 , get res: ResA---1494687543.85

I am Thread-9 , get res: ResA---1494687543.85

I am Thread-9 , get res: ResB---1494687543.85

I am Thread-9 , get res: ResB---1494687543.85

I am Thread-9 , get res: ResA---1494687544.06

I am Thread-7 , get res: ResA---1494687544.06

I am Thread-7 , get res: ResB---1494687544.06

I am Thread-7 , get res: ResB---1494687544.06

I am Thread-7 , get res: ResA---1494687544.26

I am Thread-3 , get res: ResA---1494687544.26

I am Thread-3 , get res: ResB---1494687544.26

I am Thread-3 , get res: ResB---1494687544.26

I am Thread-3 , get res: ResA---1494687544.46

从上面的例子来看,死锁的问题被完美的解决掉了。

最后,总结下递归锁:

在python中,如果同一个线程需要多次去访问同一个共享资源,这个时候,就可以使用递归锁(RLock),递归锁的内部,维护了一个Lock对象和一个counter计数变量,counter记录了acquire的次数,从而使得资源可以被多次require。直到一个线程所有的acquire都被release,其他的线程才能获得资源。

所以说RLock可以完全代替Lock,能用递归锁尽量用递归锁!

本文转自苏浩智 51CTO博客,原文链接:http://blog.51cto.com/suhaozhi/1925393,如需转载请自行联系原作者

11.python并发入门(part4 死锁与递归锁)相关推荐

  1. 11.python并发入门(part9 多进程模块multiprocessing基本用法)

    一.回顾多继承的概念. 由于GIL(全局解释器锁)的存在,在python中无法实现真正的多线程(一个进程里的多个线程无法在cpu上并行执行),如果想充分的利用cpu的资源,在python中需要使用进程 ...

  2. 11.python并发入门(part3 多线程与互斥锁)

    一.锁的概念. 锁,通常被用来实现共享数据的访问,为每一个共享的数据,创建一个Lock对象(一把锁),当需要访问这个共享的资源时,可以调用acquire方法来获取一个锁的对象,当共享资源访问结束后,在 ...

  3. 11.python并发入门(part8 基于线程队列实现生产者消费者模型)

    一.什么是生产者消费者模型? 生产者就是生产数据的线程,消费者指的就是消费数据的线程. 在多线程开发过程中,生产者的速度比消费者的速度快,那么生产者就必须等待消费者把数据处理完,生产者才会产生新的数据 ...

  4. 11.python并发入门(part5 event对象)

    一.引入event. 每个线程,都是一个独立运行的个体,并且每个线程的运行状态是无法预测的. 如果一个程序中有很多个线程,程序的其他线程需要判断某个线程的运行状态,来确定自己下一步要执行哪些操作. t ...

  5. Python之路 34:并发与并行、锁(GIL、同步锁、死锁与递归锁)、信号量、线程队列、生消模型、进程(基础使用、进程通信、进程池、回调函数)、协程

    内容: 同步锁 死锁.递归锁 信号量和同步对象(暂时了解即可) 队列------生产者和消费者模型 进程(基础使用.进程通信.进程池.回调函数) 协程 一.并发并行与同步异步的概念 1.1.并发和并行 ...

  6. 学习笔记(28):Python网络编程并发编程-死锁与递归锁

    立即学习:https://edu.csdn.net/course/play/24458/296445?utm_source=blogtoedu 1.死锁(Lock()的局限性) 知识点:Lock()只 ...

  7. Python之进程+线程+协程(并发与并行、GIL锁、同步锁、死锁、递归锁)

    文章目录 一.并发与并行 二.同步与异步 三.线程锁 1.GIL全局解释器锁 2.同步锁 3.死锁 4.递归锁 在Python中GIL解释器锁.同步锁.死锁.递归锁都是什么?怎么这么多锁,它们都是用来 ...

  8. python中的多线程 GIL(全局解释器锁) 死锁与递归锁

    1.什么的是线程 在程序里一个执行路线就叫做线程,线程是程序执行的最小单位 2.多线程的优点 使用线程可以把占据长时间的程序中的任务放到后台去处理. 在处理I/O密集程序的运行速度可能加快(ps:计算 ...

  9. GIL+死锁与递归锁+信号量+event事件

    GIL全局解释器锁: GIL本质就是一把互斥锁,相当于执行权限,每个进程内都会存在一把GIL,同一进程内的多个线程 必须抢到GIL之后才能使用Cpython解释器来执行自己的代码,即同一进程下的多个线 ...

最新文章

  1. HTML label的隐藏,html – 带有可见的隐藏元素:after – CSS
  2. 2097352GB地图数据,AI技术酷炫渲染,《微软飞行模拟器》游戏即将上线
  3. Struts2和SpringMVC的执行流程。
  4. 特征选择--文本分类: 信息增益
  5. 操作系统:基于页面置换算法的缓存原理详解(下)
  6. java 数组 push pop_JavaScript学习笔记:数组的push()、pop()、shift()和unshift()方法
  7. mybatis resultmap嵌套_Java面试专题之九:Mybatis面试5个大概率被问到的问题
  8. 你的第一个Django程序
  9. c++求n的几次方_2.七年级数学:怎么求mn+mn的值?完全平方公式,用配方法
  10. 关于文件的INode与Java中的文件操作接口
  11. 【Spring第四篇】DI注入以及c、p命名空间
  12. 【论文笔记】基于2-channel network的图片相似度判别-CVPR 2015
  13. SpreadJS 纯前端表格控件应用案例:立信智能审计云平台(SACP)
  14. 百度贴吧个人主页_回望人人网/新浪博客/百度贴吧时
  15. 沉默的潜意识音频Creator软件
  16. ELK抓取AWS-ELB日志的logstash配置文件
  17. 【2016】心有猛虎,细嗅蔷薇In me the tiger sniffs the rose
  18. RTF转换为HTML格式(java)
  19. 页高速缓存(page cache学习)
  20. 《CAT NAUGHTY CARP》App Store 隱私政策網址(URL)

热门文章

  1. Spring4.X系列之AOP-@AspectJ
  2. Oracle优化03-Latch和等待
  3. 普通平键的主要尺寸有_工字钢尺寸大全
  4. 学习笔记——Numpy基本操作(二)
  5. mysql密码错误 mac_MAC下MYSQL5.7.17连接不上提示密码错解决步骤
  6. Python2和Python3除法差别
  7. html实现ppt缩放效果,Powerpoint 2016缩放功能详解(附案例)
  8. python3 time
  9. java 面试题 简书_java面试题
  10. mysql 触发器 分行_mysql 触发器