python并发编程之多线程2死锁与递归锁,信号量等
一、死锁现象与递归锁
进程也是有死锁的
所谓死锁: 是指两个或两个以上的进程或线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,
这些永远在互相等待的进程称为死锁进程
如下就是死锁
死锁-------------------
from threading import Thread,Lock,RLock
import time
mutexA = Lock()
mutexB = Lock()
class MyThread(Thread):
def run(self):
self.f1()
self.f2()
def f1(self):
mutexA.acquire()
print('\033[33m%s 拿到A锁 '%self.name)
mutexB.acquire()
print('\033[45%s 拿到B锁 '%self.name)
mutexB.release()
mutexA.release()
def f2(self):
mutexB.acquire()
print('\033[33%s 拿到B锁 ' % self.name)
time.sleep(1) #睡一秒就是为了保证A锁已经被别人那到了
mutexA.acquire()
print('\033[45m%s 拿到B锁 ' % self.name)
mutexA.release()
mutexB.release()
if __name__ == '__main__':
for i in range(10):
t = MyThread()
t.start() #一开启就会去调用run方法 死锁现象
那么怎么解决死锁现象呢?
解决方法,递归锁:在Python中为了支持在同一线程中多次请求同一资源,python提供了可重入锁RLock。
这个RLock内部维护着一个Lock和一个counter变量,counter记录了acquire的次数,从而使得资源可以被多次require。
直到一个线程所有的acquire都被release,其他的线程才能获得资源。上面的例子如果使用RLock代替Lock,则不会发生死锁
mutexA=mutexB=threading.RLock() #一个线程拿到锁,counter加1,该线程内又碰到加锁的情况,
则counter继续加1,这期间所有其他线程都只能等待,等待该线程释放所有锁,即counter递减到0为止
# 2.解决死锁的方法--------------递归锁
from threading import Thread,Lock,RLock
import time
mutexB = mutexA = RLock()
class MyThread(Thread):
def run(self):
self.f1()
self.f2()
def f1(self):
mutexA.acquire()
print('\033[33m%s 拿到A锁 '%self.name)
mutexB.acquire()
print('\033[45%s 拿到B锁 '%self.name)
mutexB.release()
mutexA.release()
def f2(self):
mutexB.acquire()
print('\033[33%s 拿到B锁 ' % self.name)
time.sleep(1) #睡一秒就是为了保证A锁已经被别人拿到了
mutexA.acquire()
print('\033[45m%s 拿到B锁 ' % self.name)
mutexA.release()
mutexB.release()
if __name__ == '__main__':
for i in range(10):
t = MyThread()
t.start() #一开启就会去调用run方法 解决死锁
二、信号量Semaphore(其实也是一把锁)
Semaphore管理一个内置的计数器
Semaphore与进程池看起来类似,但是是完全不同的概念。
进程池:Pool(4),最大只能产生四个进程,而且从头到尾都只是这四个进程,不会产生新的。
信号量:信号量是产生的一堆进程/线程,即产生了多个任务都去抢那一把锁
from threading import Thread,Semaphore,currentThread
import time,random
sm = Semaphore(5) #运行的时候有5个人
def task():
sm.acquire()
print('\033[42m %s去洗手间'%currentThread().getName())
time.sleep(random.randint(1,3))
print('\033[31m %s上完厕所走了'%currentThread().getName())
sm.release()
if __name__ == '__main__':
for i in range(20): #开了10个线程 ,这20人都要去洗手间
t = Thread(target=task)
t.start() Semaphore举例
hread-1去洗手间
Thread-2去洗手间
Thread-3去洗手间
Thread-4去洗手间
Thread-5去洗手间
Thread-3去完洗手间走了
Thread-6去洗手间
Thread-1去完洗手间走了
Thread-7去洗手间
Thread-2去完洗手间走了
Thread-8去洗手间
Thread-6去完洗手间走了
Thread-5去完洗手间走了
Thread-4去完洗手间走了
Thread-9去洗手间
Thread-10去洗手间
Thread-11去洗手间
Thread-9去完洗手间走了
Thread-12去洗手间
Thread-7去完洗手间走了
Thread-13去洗手间
Thread-10去完洗手间走了
Thread-8去完洗手间走了
Thread-14去洗手间
Thread-15去洗手间
Thread-12去完洗手间走了
Thread-11去完洗手间走了
Thread-16去洗手间
Thread-17去洗手间
Thread-14去完洗手间走了
Thread-15去完洗手间走了
Thread-17去完洗手间走了
Thread-18去洗手间
Thread-19去洗手间
Thread-20去洗手间
Thread-13去完洗手间走了
Thread-20去完洗手间走了
Thread-16去完洗手间走了
Thread-18去完洗手间走了
Thread-19去完洗手间走了 运行结果
三、Event
如果有线程等待一个Event对象, 而这个Event对象的标志为假,那么这个线程将会被一直阻塞直至该标志为真。一个线程如果将一个Event对象的信号标志设置为真,它将唤醒所有等待这个Event对象的线程。如果一个线程等待一个已经被设置为真的Event对象,那么它将忽略这个事件, 继续执行
from threading import Event
Event.isSet() #返回event的状态值
Event.wait() #如果 event.isSet()==False将阻塞线程;
Event.set() #设置event的状态值为True,所有阻塞池的线程激活进入就绪状态, 等待操作系统调度;
Event.clear() #恢复
例如1.,有多个工作线程尝试链接MySQL,我们想要在链接前确保MySQL服务正常才让那些工作线程去连接MySQL服务器,如果连接不成功,都会去尝试重新连接。那么我们就可以采用threading.Event机制来协调各个工作线程的连接操作
#首先定义两个函数,一个是连接数据库
# 一个是检测数据库
from threading import Thread,Event,currentThread
import time
e = Event()
def conn_mysql():
'''链接数据库'''
count = 1
while not e.is_set(): #当没有检测到时候
if count >3: #如果尝试次数大于3,就主动抛异常
raise ConnectionError('尝试链接的次数过多')
print('\033[45m%s 第%s次尝试'%(currentThread(),count))
e.wait(timeout=1) #等待检测(里面的参数是超时1秒)
count+=1
print('\033[44m%s 开始链接...'%(currentThread().getName()))
def check_mysql():
'''检测数据库'''
print('\033[42m%s 检测mysql...' % (currentThread().getName()))
time.sleep(5)
e.set()
if __name__ == '__main__':
for i in range(3): #三个去链接
t = Thread(target=conn_mysql)
t.start()
t = Thread(target=check_mysql)
t.start() 详看
2.例如2,红绿灯的例子
from threading import Thread,Event,currentThread
import time
e = Event()
def traffic_lights():
'''红绿灯'''
time.sleep(5)
e.set()
def car():
'''车'''
print('\033[42m %s 等绿灯\033[0m'%currentThread().getName())
e.wait()
print('\033[44m %s 车开始通行' % currentThread().getName())
if __name__ == '__main__':
for i in range(10):
t = Thread(target=car) #10辆车
t.start()
traffic_thread = Thread(target=traffic_lights) #一个红绿灯
traffic_thread.start() 红绿灯
四、定时器(Timer)
指定n秒后执行某操作
from threading import Timer
def func(n):
print('hello,world',n)
t = Timer(3,func,args=(123,)) #等待三秒后执行func函数,因为func函数有参数,那就再传一个参数进去
t.start()
五、线程queue
queue队列 :使用import queue,用法与进程Queue一样
queue.Queue(maxsize=0) #先进先出
# 1.队列-----------
import queue
q = queue.Queue(3) #先进先出
q.put('first')
q.put('second')
q.put('third')
print(q.get())
print(q.get())
print(q.get())
queue.LifoQueue(maxsize=0)#先进后出
# 2.堆栈----------
q = queue.LifoQueue() #先进后出(或者后进先出)
q.put('first')
q.put('second')
q.put('third')
q.put('for')
print(q.get())
print(q.get())
print(q.get())
queue.PriorityQueue(maxsize=0) #存储数据时可设置优先级的队列
# ----------------
'''3.put进入一个元组,元组的第一个元素是优先级
(通常也可以是数字,或者也可以是非数字之间的比较)
数字越小,优先级越高'''
q = queue.PriorityQueue()
q.put((20,'a'))
q.put((10,'b')) #先出来的是b,数字越小优先级越高嘛
q.put((30,'c'))
print(q.get())
print(q.get())
print(q.get())
六、多线程性能测试
1.多核也就是多个CPU
(1)cpu越多,提高的是计算的性能
(2)如果程序是IO操作的时候(多核和单核是一样的),再多的cpu也没有意义。
2.实现并发
第一种:一个进程下,开多个线程
第二种:开多个进程
3.多进程:
优点:可以利用多核
缺点:开销大
4.多线程
优点:开销小
缺点:不可以利用多核
5多进程和多进程的应用场景
1.计算密集型:也就是计算多,IO少
如果是计算密集型,就用多进程(如金融分析等)
2.IO密集型:也就是IO多,计算少
如果是IO密集型的,就用多线程(一般遇到的都是IO密集型的)
下例子练习:
# 计算密集型的要开启多进程
from multiprocessing import Process
from threading import Thread
import time
def work():
res = 0
for i in range(10000000):
res+=i
if __name__ == '__main__':
l = []
start = time.time()
for i in range(4):
p = Process(target=work) #1.9371106624603271 #可以利用多核(也就是多个cpu)
# p = Thread(target=work) #3.0401737689971924
l.append(p)
p.start()
for p in l:
p.join()
stop = time.time()
print('%s'%(stop-start)) 计算密集型
# I/O密集型要开启多线程
from multiprocessing import Process
from threading import Thread
import time
def work():
time.sleep(3)
if __name__ == '__main__':
l = []
start = time.time()
for i in range(400):
# p = Process(target=work) #34.9549994468689 #因为开了好多进程,它的开销大,花费的时间也就长了
p = Thread(target=work) #2.2151265144348145 #当开了多个线程的时候,它的开销小,花费的时间也小了
l.append(p)
p.start()
for i in l :
i.join()
stop = time.time()
print('%s'%(stop-start)) I/O密集型
python并发编程之多线程2死锁与递归锁,信号量等的更多相关文章
- python并发编程之多线程2---(死锁与递归锁,信号量等)
一.死锁现象与递归锁 进程也是有死锁的 所谓死锁: 是指两个或两个以上的进程或线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用, 它们都将无法推进下去.此时称系统处于死锁状态或系统 ...
- Python并发编程04 /多线程、生产消费者模型、线程进程对比、线程的方法、线程join、守护线程、线程互斥锁
Python并发编程04 /多线程.生产消费者模型.线程进程对比.线程的方法.线程join.守护线程.线程互斥锁 目录 Python并发编程04 /多线程.生产消费者模型.线程进程对比.线程的方法.线 ...
- Python并发编程之多线程使用
目录 一 开启线程的两种方式 二 在一个进程下开启多个线程与在一个进程下开启多个子进程的区别 三 练习 四 线程相关的其他方法 五 守护线程 六 Python GIL(Global Interpret ...
- python 并发编程之多线程
一.线程理论 1.什么是线程 多线程(即多个控制线程)的概念是,在一个进程中存在多个线程,多个线程共享该进程的地址空间,相当于一个车间内有多条流水线,都共用一个车间的资源. 所以,进程只是用来把资 ...
- 30 python 并发编程之多线程
一 threading模块介绍 multiprocess模块的完全模仿了threading模块的接口,二者在使用层面,有很大的相似性,因而不再详细介绍 官网链接:https://docs.python ...
- 三 python并发编程之多线程-重点
一 threading模块介绍 multiprocess模块的完全模仿了threading模块的接口,二者在使用层面,有很大的相似性,因而不再详细介绍 二 开启线程的两种方式 #方式一 from th ...
- 第十篇.4、python并发编程之多线程
一 threading模块介绍 multiprocess模块的完全模仿了threading模块的接口,二者在使用层面,有很大的相似性,因而不再详细介绍 官网链接:https://docs.python ...
- 36、python并发编程之多线程(操作篇)
目录: 一 threading模块介绍 二 开启线程的两种方式 三 在一个进程下开启多个线程与在一个进程下开启多个子进程的区别 四 练习 五 线程相关的其他方法 六 守护线程 七 Python GIL ...
- python并发编程之线程(二):死锁和递归锁&信号量&定时器&线程queue&事件evevt
一 死锁现象与递归锁 进程也有死锁与递归锁,在进程那里忘记说了,放到这里一切说了额 所谓死锁: 是指两个或两个以上的进程或线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将 ...
随机推荐
- tool class
在Java中,工具类定义了一组公共方法,这篇文章将介绍Java中使用最频繁及最通用的Java工具类.以下工具类.方法按使用流行度排名,参考数据来源于Github上随机选取的5万个开源项目源码. 一. ...
- js判断undefined类型,undefined,null, 的区别详细解析
js判断undefined类型 今天使用showModalDialog打开页面,返回值时.当打开的页面点击关闭按钮或直接点浏览器上的关闭则返回值是undefined所以自作聪明判断 var reVal ...
- ICCV 2015 B-CNN细粒度分类
哈哈,好久没写博客了....最近懒癌发作~~主要是因为心情不太好啊,做什么事情都不太顺心,不过已经过去啦.最近一直忙着公司的项目,想用这个网络,就给大家带来了的这篇文章.可能比较老,来自ICCV 20 ...
- Django模型系统——ORM中跨表、聚合、分组、F、Q
核心知识点: 1.明白表之间的关系 2.根据关联字段确定正反向,选择一种方式 在Django的ORM种,查询既可以通过查询的方向分为正向查询和反向查询,也可以通过不同的对象分为对象查询和Queryse ...
- 第1条:确认自己所用的Python版本
很多电脑都预装了多个版本的标准CPython运行时环境,然而,在命令行中输入默认的python命令之后,究竟会执行哪一个版本无法肯定. python通常是python2.7的别名,但也有可能是pyth ...
- J.U.C重入锁
ReentrantLock重入锁 ReentrantLock是Java并发包中互斥锁,它有公平锁和非公平锁两种实现方式, 重入的意思就是,如果已经获得了锁,如果执行期间还需要获得这个锁的话,会直接获得 ...
- GUI菜单——菜单条、菜单、子条目之间关系
菜单:注意区分三个概念:菜单条.菜单.菜单项 将菜单条添加到窗体,菜单条下面包括菜单,菜单下面可以使菜单或者菜单项 菜单项是最后一个.菜单后面有三角标示. 菜单条[文件] 子菜单--子条目 子条目 示 ...
- [原创]java WEB学习笔记22:MVC案例完整实践(part 3)---多个请求对应一个Servlet解析
本博客为原创:综合 尚硅谷(http://www.atguigu.com)的系统教程(深表感谢)和 网络上的现有资源(博客,文档,图书等),资源的出处我会标明 本博客的目的:①总结自己的学习过程,相当 ...
- Fermat’s Chirstmas Theorem (素数打表的)
Fermat’s Chirstmas Theorem ...
- mysql 主从,主主,主主复制时的主键冲突解决
原理:slave 的i/o thread ,不断的去master抓取 bin_log, 写入到本地relay_log 然后sql thread不断的更新slave的数据 把主服务器所有的数据复制给从服 ...