GILEvent事件信号量死锁递归锁
Posted buzaiyicheng
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了GILEvent事件信号量死锁递归锁相关的知识,希望对你有一定的参考价值。
GIL全局解释器锁
Python解释器有很多种,最常见的就是CPython解释器
GIL本质也是一把互斥锁:将并发变成串行,牺牲效率保证数据的安全?
用来阻止同一个进程下的 多个线程的 同时执行
(同一个进程内的多个线程无法实现并行,但是可以实现并发)
Python的多线程没法利用多核优势,是不是就没有用了?
GIL的存在是因为CPython解释器的内存管理不是线程安全的
垃圾回收机制:
a. 引用计数
b. 标记清除
c. 分代回收
研究Python的多线程是否有用,需要分情况讨论:
(假设有四个任务,每个任务需要10s 处理完成)? 1. 计算密集型?: 单核情况下?: ?开线程更省资源(原因是开进程需要申请内存空间等操作,浪费时间) 多核情况下: 开进程处理 10s 开线程处理 40s 2. I/O密集型: ?????单核情况下: 开线程更省资源(原因和计算密集型的一样) 多核情况下: ???开线程更省资源 ?(原因是 I/O秘籍情况下,线程工作方式为 切换+保存 ?进程申请内存空间,是要消耗很长时间的, 而?线程的切换+保存基本不消耗时间) ? python的多线程到底有没有用 需要看情况而定 并且肯定是有用的 多进程+多线程配合使用?
# I/O密集型 from multiprocessing import Process from threading import Thread import threading import os,time ? def work(): time.sleep(2) ? if __name__ == ‘__main__‘: l=[] print(os.cpu_count()) # 本机为8核 start=time.time() ? for i in range(400): p=Process(target=work) # 耗时9.506524085998535s多,大部分时间耗费在创建进程上 # p=Thread(target=work) # 耗时 2.0459132194519043 s多 l.append(p) p.start() ? for p in l: p.join() ? stop=time.time() print(‘run time is %s‘ %(stop-start))
# 计算密集型 from multiprocessing import Process from threading import Thread import os,time ? def work(): res=0 for i in range(100000000): res *= i ? if __name__ == ‘__main__‘: l=[] print(os.cpu_count()) # 本机为8核 start=time.time() ? for i in range(8): p=Process(target=work) # 耗时 8.460614204406738s # p=Thread(target=work) # 耗时 34.822699308395386s l.append(p) p.start() ? for p in l: p.join() ? stop=time.time() print(‘run time is %s‘ %(stop-start))
死锁
1. 代码表示下一步要acquire一个锁 2. ?但是两个线程互相拿着对方需要的锁,于是程序就hang到这儿了 这种情况就是死锁
from threading import Thread, Lock, current_thread import time ? mutexA = Lock() mutexB = Lock()?? ? class MyThread(Thread): ? # 创建线程自动触发run方法 run方法内调用func1 func2相当于也是自动触发 def run(self): self.func1() self.func2() ? def func1(self): mutexA.acquire() print(‘%s抢到了A锁‘%self.name) # self.name等价于current_thread().name mutexB.acquire() print(‘%s抢到了B锁‘%self.name) mutexB.release() print(‘%s释放了B锁‘%self.name) mutexA.release() print(‘%s释放了A锁‘%self.name) ? def func2(self): mutexB.acquire() print(‘%s抢到了B锁‘%self.name) time.sleep(1) mutexA.acquire() print(‘%s抢到了A锁‘ % self.name) mutexA.release() print(‘%s释放了A锁‘ % self.name) mutexB.release() print(‘%s释放了B锁‘ % self.name) ? for i in range(10): t = MyThread() t.start()?? ? ‘‘‘执行结果: Thread-1抢到了A锁 Thread-1抢到了B锁 Thread-1释放了B锁 Thread-1释放了A锁 Thread-1抢到了B锁 Thread-2抢到了A锁 ?(程序到这儿并没有结束,而是一直hang着...) ? ???‘‘‘
递归锁
Rlock可以被第一个抢到锁的人连续的acquire和release
每acquire一次锁身上的计数加1
每release一次锁身上的计数减1
只要锁的计数不为0 其他人都不能抢
from threading import Thread, Lock, current_thread, RLock import time ? # mutexA = Lock() # mutexB = Lock()?? mutexA = mutexB = RLock() # A B现在是同一把锁? ? class MyThread(Thread): ? # 创建线程自动触发run方法 run方法内调用func1 func2相当于也是自动触发 def run(self): self.func1() self.func2() ? def func1(self): mutexA.acquire() print(‘%s抢到了A锁‘%self.name) # self.name等价于current_thread().name mutexB.acquire() print(‘%s抢到了B锁‘%self.name) mutexB.release() print(‘%s释放了B锁‘%self.name) mutexA.release() print(‘%s释放了A锁‘%self.name) ? def func2(self): mutexB.acquire() print(‘%s抢到了B锁‘%self.name) time.sleep(1) mutexA.acquire() print(‘%s抢到了A锁‘ % self.name) mutexA.release() print(‘%s释放了A锁‘ % self.name) mutexB.release() print(‘%s释放了B锁‘ % self.name) ? for i in range(2): t = MyThread() t.start()?? ? ‘‘‘执行结果: Thread-1抢到了A锁 Thread-1抢到了B锁 Thread-1释放了B锁 Thread-1释放了A锁 Thread-1抢到了B锁 Thread-1抢到了A锁 Thread-1释放了A锁 Thread-1释放了B锁 Thread-2抢到了A锁 Thread-2抢到了B锁 Thread-2释放了B锁 Thread-2释放了A锁 Thread-2抢到了B锁 Thread-2抢到了A锁 Thread-2释放了A锁 Thread-2释放了B锁 ??‘‘‘
Event事件
前言
三两赛车准备出发,但是要等信号灯打出绿灯信号时,才出发
Event事件
事件处理的机制。
全局定义了一个内置标志Flag,
Flag默认值为 False,那么当程序执行 event.wait方法时就会阻塞,
如果Flag值为True,那么event.wait 方法时便不再阻塞。
涉及的方法:
set(): 将标志设为True,并通知所有处于等待阻塞状态的线程恢复运行状态。
clear(): 将标志设为False。
wait(timeout): 如果标志为True将立即返回,否则阻塞线程至等待阻塞状态,等待其他线程调用set()。
isSet(): 获取内置标志状态,返回True或False。
from threading import Event,Thread import time ? # 先生成一个event对象 e = Event() ? def light(): print(‘红灯正亮着‘) time.sleep(3) e.set() # 发信号 print(‘绿灯亮了‘) ? def car(name): print(‘%s正在等红灯‘%name) e.wait() # 等待信号 print(‘%s加油门飙车了‘%name) ? t = Thread(target=light) t.start() ? for i in range(3): t = Thread(target=car,args=(‘伞兵%s‘%i,)) t.start() ? ‘‘‘执行结果: 红灯正亮着 伞兵0正在等红灯 伞兵1正在等红灯 伞兵2正在等红灯 (此处hang3s,因为sleep了3s)? 绿灯亮了 伞兵1加油门飙车了 伞兵0加油门飙车了 伞兵2加油门飙车了 ?‘‘‘
信号量
信号量semaphore 是一个变量,控制着对公共资源或者临界区的访问。 ?信号量维护着一个计数器,指定可同时访问资源或者进入临界区的线程数。 实现机制:?? 每次有一个线程获得信号量时,计数器-1。 ?若计数器为0,其他线程就停止访问信号量,直到另一个线程释放信号量。
# 信号量代码示例 from threading import Semaphore,Thread import time import random ? sm = Semaphore(5) # 造了一个含有五个的坑位的公共厕所 ? def task(name): sm.acquire() print(‘%s占了一个坑位‘%name) time.sleep(random.random()) sm.release() print(‘%s释放了一个坑位‘ % name) ? for i in range(4): t = Thread(target=task,args=(i,)) t.start() ? ‘‘‘执行结果: 0占了一个坑位 1占了一个坑位 2占了一个坑位 3占了一个坑位 1释放了一个坑位 0释放了一个坑位 2释放了一个坑位 3释放了一个坑位 ?‘‘‘??
补充讲解三种队列
同一个进程下的多个线程本来就是数据共享 为什么还要用队列 因为队列是管道+锁 使用队列你就不需要自己手动操作锁的问题 因为锁操作的不好极容易产生死锁现象
import Queue ? ?q = queue.Queue() q.put(‘hahha‘) print(q.get()) # 执行结果:hahha ?? q = queue.LifoQueue() q.put(1) q.put(2) q.put(3) print(q.get()) # 执行结果:3? ?? q = queue.PriorityQueue() # 数字越小 优先级越高 q.put((10,‘haha‘)) q.put((100,‘hehehe‘)) q.put((0,‘xxxx‘)) q.put((-10,‘yyyy‘)) print(q.get()) # 执行结果:(-10, ‘yyyy‘)?
以上是关于GILEvent事件信号量死锁递归锁的主要内容,如果未能解决你的问题,请参考以下文章
并发编程---死锁||递归锁---信号量---Event事件---定时器
Python学习第24篇:死锁,递归锁,信号量,Event事件,线程Queue
python全栈开发基础第二十五篇死锁,递归锁,信号量,Event事件,线程Queue
Python GIL锁 死锁 递归锁 event事件 信号量
并发编程 - 线程 - 1.互斥锁/2.GIL解释器锁/3.死锁与递归锁/4.信号量/5.Event事件/6.定时器
网络编程基础--多线程---concurrent.futures 模块---事件Event---信号量Semaphore---定时器Timer---死锁现象 递归锁----线程队列queue(示例代码