Python--同步锁(互斥锁)、死锁(状态)、递归锁、信号量、Event对象
同步锁/互斥锁 (Lock)
import time
import threading def sub():
global num #在每个线程中都获取这个全局变量
#num-=1 temp=num
time.sleep(0.1)
num =temp-1 # 对此公共变量进行-1操作 num = 100 #设定一个共享变量 thread_list = [] for i in range(100):
t = threading.Thread(target=sub)
t.start()
thread_list.append(t) for t in thread_list: #等待所有线程执行完毕
t.join() print('Result: ', num)
运行结果:
99
我们想要的结果是多线程实现递减至0 ,但是很明显没有实现。
下面这段代码更改了一下睡眠时间,依然结果不会是0 。
import time
import threading def sub():
global num #在每个线程中都获取这个全局变量
#num-=1 temp=num
time.sleep(0.001)
num =temp-1 # 对此公共变量进行-1操作 num = 100 #设定一个共享变量 thread_list = [] for i in range(100):
t = threading.Thread(target=sub)
t.start()
thread_list.append(t) for t in thread_list: #等待所有线程执行完毕
t.join() print('Result: ', num)
运行结果:
大于0 但是小于100的数
锁通常被用来实现对共享资源的同步访问。
import threading R=threading.Lock() #为每一个共享资源创建一个Lock对象 R.acquire() #当你需要访问该资源时,调用acquire方法来获取锁对象(如果其它线程已经获得了该锁,则当前线程需等待其被释放)
'''
对公共数据的操作
'''
R.release() #待资源访问完后,再调用release方法释放锁:
import time
import threading def sub():
global num #在每个线程中都获取这个全局变量 lock.acquire()
temp=num
time.sleep(0.001)
num =temp-1 # 对此公共变量进行-1操作
lock.release() num = 100 #设定一个共享变量
thread_list = []
lock=threading.Lock() for i in range(100):
t = threading.Thread(target=sub)
t.start()
thread_list.append(t) for t in thread_list: #等待所有线程执行完毕
t.join() print('Result: ', num)
运行结果:
Result: 0
这样就解决了我们的问题。
为什么有了GIL,还需要线程同步? 多线程环境下必须存在资源的竞争,那么如何才能保证同一时刻只有一个线程对共享资源进行存取? 加锁, 对, 加锁可以保证存取操作的唯一性, 从而保证同一时刻只有一个线程对共享数据存取.
死锁与递归锁
import time
import threading
class Mythread(threading.Thread):
def __init__(self):
threading.Thread.__init__(self) def run(self):
self.foo() def foo(self):
locka.acquire()
print('I am %s got locka---%s'%(self.name,time.ctime())) lockb.acquire()
print('I am %s got lockb---%s'%(self.name,time.ctime())) lockb.release()
locka.release() if __name__ == '__main__':
locka=threading.Lock()
lockb=threading.Lock() for i in range(10):
t=Mythread()
t.start()
运行效果:
I am Thread-1 got locka---Fri Jul 21 14:30:32 2017
I am Thread-1 got lockb---Fri Jul 21 14:30:32 2017
I am Thread-2 got locka---Fri Jul 21 14:30:32 2017
I am Thread-2 got lockb---Fri Jul 21 14:30:32 2017
I am Thread-3 got locka---Fri Jul 21 14:30:32 2017
I am Thread-3 got lockb---Fri Jul 21 14:30:32 2017
I am Thread-4 got locka---Fri Jul 21 14:30:32 2017
I am Thread-4 got lockb---Fri Jul 21 14:30:32 2017
I am Thread-5 got locka---Fri Jul 21 14:30:32 2017
I am Thread-5 got lockb---Fri Jul 21 14:30:32 2017
I am Thread-6 got locka---Fri Jul 21 14:30:32 2017
I am Thread-6 got lockb---Fri Jul 21 14:30:32 2017
I am Thread-7 got locka---Fri Jul 21 14:30:32 2017
I am Thread-7 got lockb---Fri Jul 21 14:30:32 2017
I am Thread-8 got locka---Fri Jul 21 14:30:32 2017
I am Thread-8 got lockb---Fri Jul 21 14:30:32 2017
I am Thread-9 got locka---Fri Jul 21 14:30:32 2017
I am Thread-9 got lockb---Fri Jul 21 14:30:32 2017
I am Thread-10 got locka---Fri Jul 21 14:30:32 2017
I am Thread-10 got lockb---Fri Jul 21 14:30:32 2017

所谓死锁: 是指两个或两个以上的进程或线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。
import time
import threading
class Mythread(threading.Thread):
def __init__(self):
threading.Thread.__init__(self) def run(self):
self.foo()
self.bar() def foo(self):
locka.acquire()
print('I am %s got locka---%s'%(self.name,time.ctime())) lockb.acquire()
print('I am %s got lockb---%s'%(self.name,time.ctime())) lockb.release()
locka.release() def bar(self):
lockb.acquire()
print('I am %s got lockb---%s'%(self.name,time.ctime())) locka.acquire()
print('I am %s got locka---%s'%(self.name,time.ctime())) locka.release()
lockb.release() if __name__ == '__main__':
locka=threading.Lock()
lockb=threading.Lock() for i in range(10):
t=Mythread()
t.start()
运行效果图:

解决死锁状态的递归锁出现了。
在Python中为了支持在同一线程中多次请求同一资源,python提供了可重入锁RLock。
这个RLock内部维护着一个Lock和一个counter变量,counter记录了acquire的次数,从而使得资源可以被多次require。直到一个线程所有的acquire都被release,其他的线程才能获得资源。上面的例子如果使用RLock代替Lock,则不会发生死锁:
import time
import threading class Mythread(threading.Thread):
def __init__(self):
threading.Thread.__init__(self) def run(self):
self.foo()
self.bar() def foo(self):
Rlock.acquire()
print('I am %s got locka---%s'%(self.name,time.ctime())) Rlock.acquire()
print('I am %s got lockb---%s'%(self.name,time.ctime())) Rlock.release()
Rlock.release() def bar(self):
Rlock.acquire()
print('I am %s got lockb---%s'%(self.name,time.ctime())) Rlock.acquire()
print('I am %s got locka---%s'%(self.name,time.ctime())) Rlock.release()
Rlock.release() if __name__ == '__main__':
Rlock=threading.RLock() for i in range(10):
t=Mythread()
t.start()
运行效果:
I am Thread-1 got locka---Fri Jul 21 14:42:08 2017
I am Thread-1 got lockb---Fri Jul 21 14:42:08 2017
I am Thread-2 got locka---Fri Jul 21 14:42:08 2017
I am Thread-2 got lockb---Fri Jul 21 14:42:08 2017
I am Thread-2 got lockb---Fri Jul 21 14:42:08 2017
I am Thread-2 got locka---Fri Jul 21 14:42:08 2017
I am Thread-1 got lockb---Fri Jul 21 14:42:08 2017
I am Thread-1 got locka---Fri Jul 21 14:42:08 2017
I am Thread-3 got locka---Fri Jul 21 14:42:08 2017
I am Thread-3 got lockb---Fri Jul 21 14:42:08 2017
I am Thread-4 got locka---Fri Jul 21 14:42:08 2017
I am Thread-4 got lockb---Fri Jul 21 14:42:08 2017
I am Thread-5 got locka---Fri Jul 21 14:42:08 2017
I am Thread-5 got lockb---Fri Jul 21 14:42:08 2017
I am Thread-3 got lockb---Fri Jul 21 14:42:08 2017
I am Thread-3 got locka---Fri Jul 21 14:42:08 2017
I am Thread-6 got locka---Fri Jul 21 14:42:08 2017
I am Thread-6 got lockb---Fri Jul 21 14:42:08 2017
I am Thread-6 got lockb---Fri Jul 21 14:42:08 2017
I am Thread-6 got locka---Fri Jul 21 14:42:08 2017
I am Thread-4 got lockb---Fri Jul 21 14:42:08 2017
I am Thread-4 got locka---Fri Jul 21 14:42:08 2017
I am Thread-7 got locka---Fri Jul 21 14:42:08 2017
I am Thread-7 got lockb---Fri Jul 21 14:42:08 2017
I am Thread-7 got lockb---Fri Jul 21 14:42:08 2017
I am Thread-7 got locka---Fri Jul 21 14:42:08 2017
I am Thread-9 got locka---Fri Jul 21 14:42:08 2017
I am Thread-9 got lockb---Fri Jul 21 14:42:08 2017
I am Thread-9 got lockb---Fri Jul 21 14:42:08 2017
I am Thread-9 got locka---Fri Jul 21 14:42:08 2017
I am Thread-5 got lockb---Fri Jul 21 14:42:08 2017
I am Thread-5 got locka---Fri Jul 21 14:42:08 2017
I am Thread-10 got locka---Fri Jul 21 14:42:08 2017
I am Thread-10 got lockb---Fri Jul 21 14:42:08 2017
I am Thread-10 got lockb---Fri Jul 21 14:42:08 2017
I am Thread-10 got locka---Fri Jul 21 14:42:08 2017
I am Thread-8 got locka---Fri Jul 21 14:42:08 2017
I am Thread-8 got lockb---Fri Jul 21 14:42:08 2017
I am Thread-8 got lockb---Fri Jul 21 14:42:08 2017
I am Thread-8 got locka---Fri Jul 21 14:42:08 2017
Semaphore(信号量)
Semaphore管理一个内置的计数器,
每当调用acquire()时内置计数器-1;
调用release() 时内置计数器+1;
计数器不能小于0;当计数器为0时,acquire()将阻塞线程直到其他线程调用release()。
实例:(同时只有5个线程可以获得semaphore,即可以限制最大连接数为5):
import threading
import time semaphore =threading.Semaphore(5) #同时只有5个线程可以获得semaphore,即可以限制最大连接数为5 def foo():
semaphore.acquire()
time.sleep(2)
print('ok')
semaphore.release() for i in range(10):
t=threading.Thread(target=foo)
t.start()
运行效果:
先同时打印5个‘ok’,过两秒再同时打印5个‘ok’
Event对象
线程的一个关键特性是每个线程都是独立运行且状态不可预测。
如果程序中的其 他线程需要通过判断某个线程的状态来确定自己下一步的操作,这时线程同步问题就 会变得非常棘手。为了解决这些问题,我们需要使用threading库中的Event对象。
在 初始情况下,Event对象中的信号标志被设置为假。
如果有线程等待一个Event对象, 而这个Event对象的标志为假,那么这个线程将会被一直阻塞直至该标志为真。
一个线程如果将一个Event对象的信号标志设置为真,它将唤醒所有等待这个Event对象的线程。
如果一个线程等待一个已经被设置为真的Event对象,那么它将忽略这个事件, 继续执行
event.isSet():返回event的状态值; event.wait():如果 event.isSet()==False将阻塞线程; event.set(): 设置event的状态值为True,所有阻塞池的线程激活进入就绪状态, 等待操作系统调度; event.clear():恢复event的状态值为False。

import threading,time event=threading.Event() def foo():
print('waitting...')
event.wait()
print('connect to redis server') for i in range(5):
t=threading.Thread(target=foo)
t.start() print('attempt to start redis server')
time.sleep(3) event.set()
运行效果:
先同时打印5个'waiting'和'attempt to start redis server',3秒后打印5个'connect to redis server'
threading.Event的wait方法还接受一个超时参数,默认情况下如果事件一直没有发生,wait方法会一直阻塞下去,而加入这个超时参数之后,如果阻塞时间超过这个参数设定的值之后,wait方法会返回。
import threading,time event=threading.Event() def foo():
print('waitting...')
event.wait(1)
print('cinnect to redis server') for i in range(5):
t=threading.Thread(target=foo)
t.start() print('attempt to start redis server')
time.sleep(3) event.set()
运行效果:
先同时打印5个‘waitting’,1个‘attempt to start redis server’,过1秒打印'cinnect to redis server',再过两秒程序运行结束
Python--同步锁(互斥锁)、死锁(状态)、递归锁、信号量、Event对象的更多相关文章
- 并发、并行、同步、异步、全局解释锁GIL、同步锁Lock、死锁、递归锁、同步对象/条件、信号量、队列、生产者消费者、多进程模块、进程的调用、Process类、
并发:是指系统具有处理多个任务/动作的能力. 并行:是指系统具有同时处理多个任务/动作的能力. 并行是并发的子集. 同步:当进程执行到一个IO(等待外部数据)的时候. 异步:当进程执行到一个IO不等到 ...
- Python之网路编程之死锁,递归锁,信号量,Event事件,线程Queue
一.死锁现象与递归锁 进程也是有死锁的 所谓死锁: 是指两个或两个以上的进程或线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用, 它们都将无法推进下去.此时称系统处于死锁状态或系统 ...
- Thread类的其他方法,同步锁,死锁与递归锁,信号量,事件,条件,定时器,队列,Python标准模块--concurrent.futures
参考博客: https://www.cnblogs.com/xiao987334176/p/9046028.html 线程简述 什么是线程?线程是cpu调度的最小单位进程是资源分配的最小单位 进程和线 ...
- python 全栈开发,Day42(Thread类的其他方法,同步锁,死锁与递归锁,信号量,事件,条件,定时器,队列,Python标准模块--concurrent.futures)
昨日内容回顾 线程什么是线程?线程是cpu调度的最小单位进程是资源分配的最小单位 进程和线程是什么关系? 线程是在进程中的 一个执行单位 多进程 本质上开启的这个进程里就有一个线程 多线程 单纯的在当 ...
- Python之路(第四十四篇)线程同步锁、死锁、递归锁、信号量
在使用多线程的应用下,如何保证线程安全,以及线程之间的同步,或者访问共享变量等问题是十分棘手的问题,也是使用多线程下面临的问题,如果处理不好,会带来较严重的后果,使用python多线程中提供Lock ...
- python全栈开发,Day42(Thread类的其他方法,同步锁,死锁与递归锁,信号量,事件,条件,定时器,队列,Python标准模块--concurrent.futures)
昨日内容回顾 线程 什么是线程? 线程是cpu调度的最小单位 进程是资源分配的最小单位 进程和线程是什么关系? 线程是在进程中的一个执行单位 多进程 本质上开启的这个进程里就有一个线程 多线程 单纯的 ...
- python多线程,event,互斥锁,死锁,递归锁,信号量
Python多线程/event 多线程-threading python的thread模块是⽐较底层的模块, python的threading模块是对thread做了⼀些包装的, 可以更加⽅便的被使⽤ ...
- Python并发编程-进程 线程 同步锁 线程死锁和递归锁
进程是最小的资源单位,线程是最小的执行单位 一.进程 进程:就是一个程序在一个数据集上的一次动态执行过程. 进程由三部分组成: 1.程序:我们编写的程序用来描述进程要完成哪些功能以及如何完成 2.数据 ...
- 26 python 初学(线程、同步锁、死锁和递归锁)
参考博客: www.cnblogs.com/yuanchenqi/articles/5733873.html 并发:一段时间内做一些事情 并行:同时做多件事情 线程是操作系统能够进行运算调度的基本单位 ...
- 第十五章、Python多线程同步锁,死锁和递归锁
目录 第十五章.Python多线程同步锁,死锁和递归锁 1. 引子: 2.同步锁 3.死锁 引子: 4.递归锁RLock 原理: 不多说,放代码 总结: 5. 大总结 第十五章.Python多线程同步 ...
随机推荐
- 组合的输出(回溯、dfs)
问题 O: [回溯法]组合的输出 题目描述 排列与组合是常用的数学方法,其中组合就是从n个元素中抽出r个元素(不分顺序且r<=n),我们可以简单地将n个元素理解为自然数1,2,…,n,从中任取r ...
- linux0.11内核源码——进程各状态切换的跟踪
准备工作 1.进程的状态有五种:新建(N),就绪或等待(J),睡眠或阻塞(W),运行(R),退出(E),其实还有个僵尸进程,这里先忽略 2.编写一个样本程序process.c,里面实现了一个函数 /* ...
- 暑期训练 CF套题
CodeForces 327A 题意:有n个数,都是0或1,然后必须执行一次操作,翻转一个区间,里面的数0变1,1变0,求最多1的数量 思路:最开始我写的最大字段和,后面好像写搓了,然后我又改成暴力, ...
- element table组件懒加载
directives : { loadmore : { bind(el, binding) { const selectWrap = el.querySelector('.el-table__body ...
- java并发编程笔记(八)——死锁
java并发编程笔记(八)--死锁 死锁发生的必要条件 互斥条件 进程对分配到的资源进行排他性的使用,即在一段时间内只能由一个进程使用,如果有其他进程在请求,只能等待. 请求和保持条件 进程已经保持了 ...
- JVM调优(二)——基于JVisualVM的可视化监控
JVM调优(二)--基于JVisualVM的可视化监控 工具路径://java/jdk1.8xxx/bin/JVisuaVM.exe 监控本地的Tomcat 监控远程Tomcat 监控普通的JAVA进 ...
- Hadoop 架构与原理
1.1. Hadoop架构 Hadoop1.0版本两个核心:HDFS+MapReduce Hadoop2.0版本,引入了Yarn.核心:HDFS+Yarn+Mapreduce Yarn是资源调度框 ...
- spring boot 尚桂谷学习笔记09 数据访问
springboot 与数据库访问 jdbc, mybatis, spring data jpa, 1.jdbc原生访问 新建项目 使用 springboot 快速构建工具 选中 web 组件 sq ...
- git使用记录七:对工作区和暂存区的一些操作场景
比较暂存区和HEAD所含文件的差异? 操作场景如下: 修改readme.md 文档 vi readme.md 加入到暂存区域 git add readme.md 使用git diff -cached ...
- git使用记录四:.git分析
git使用记录四: .git 查看.git 目录下的文件 soaeon@DESKTOP-FUJJTHR MINGW64 /f/gitstudy/.git (GIT_DIR!) $ ls -al tot ...