Python学习线程间同步之二
1、Barrier
# Barrier 可以理解为路障或者道闸;
# 作用:等待所有的线程到齐后开始放行;
broken 如果屏障状态处于被打破的状态,返回True;
abort() 将屏障置于broken状态,等待中的线程或者调用等待方法的线程中都会抛出BrokenBarrierError异常,等待中的线程无效,不会被计入参与方直到屏障被reset;
reset() 恢复屏障;重新开始拦截;
# 实例代码:
from threading import Thread, Lock, RLock, Event, Condition, Barrier import threading import time import logging import random FORMAT = "%(asctime)-15s\t [%(threadName)s %(thread)8d] %(message)s" logging.basicConfig(level=10, format=FORMAT) barrier = Barrier(3) def worker(barrier: Barrier): logging.info('waiting for {} threads '.format(barrier.n_waiting)) try: barrier_id=barrier.wait() logging.info('after barrier {}'.format(barrier_id)) except threading.BrokenBarrierError: logging.info('Broken Barrier. run') for i in range(1,6): if i == 2: barrier.abort() elif i == 3: barrier.reset() threading.Event().wait(1) threading.Thread(target=worker,name='worker-{}'.format(i),args=(barrier,)).start() print(i,barrier.n_waiting) '''
运行结果分析: 1 1 # 等待中的线程为1 2018-11-06 16:52:10,256 [worker-1 3620] waiting for 0 threads 2018-11-06 16:52:10,257 [worker-1 3620] Broken Barrier. run 2018-11-06 16:52:11,257 [worker-2 5800] waiting for 0 threads 2018-11-06 16:52:11,257 [worker-2 5800] Broken Barrier. run 2 0 # i=2的时候由于barrier被打破,则等待的线程无效,不会被计入wait参与方; 2018-11-06 16:52:12,258 [worker-3 5956] waiting for 0 threads 3 1 # i=3的时候barrier被修复,线程从新被计入wait方法 4 2 2018-11-06 16:52:13,259 [worker-4 7612] waiting for 1 threads 2018-11-06 16:52:14,259 [worker-5 10012] waiting for 2 threads 5 0 # i=5的时候 参与方线程为3,4,5 达到barrier放行的数目,所有线程都放行,所以等待线程为0 2018-11-06 16:52:14,260 [worker-5 10012] after barrier 2 2018-11-06 16:52:14,260 [worker-4 7612] after barrier 1 2018-11-06 16:52:14,260 [worker-3 5956] after barrier 0 '''
2、semaphore
# semaphore 信号量与Lock类似,信号量对象内部维护一个倒计数器,每一次acquire都会减1,当acquire方法发现计数器为0,就阻塞请求的线程,直到其他线程对信号量release后,计数器大于1,恢复阻塞线程;
# 由于semapore对于release归还越界问题没有限制,则会出现不可预期的效果,所有为了安全则一般使用BoundedSemaphore类;
# BoundedSemaphore类: 有界的信号量,不允许release超出初始值的范围,否则,抛出ValueError异常;
# 信号量一般用于连接池,实例代码
class Conn: def __init__(self,name): self.name=name def __repr__(self): return str(self.name) class Pool: def __init__(self,count:int): self.count=count self.pool=[self._connect("conn-{}".format(x)) for x in range(self.count)] self.sem=BoundedSemaphore(count) def _connect(self,conn_name): return Conn(conn_name) def get_conn(self): self.sem.acquire() return self.pool.pop() def return_conn(self,conn:Conn): self.pool.append(conn) self.sem.release() p=Pool(5) print(p.pool) ''' [conn-0, conn-1, conn-2, conn-3, conn-4] '''
3、数据结构与GIL
(1)什么是GIL?
# GIL 全局解释器锁;
# 由于python是一种解释性语言,则需要经过解释器将py代码编译成cpu能识别的字节码,所有就有了python解释器的存在,例如 Jpython,Cpython,PyPy等解释器;
# 由于Cpython已经成为Python的实际标准,而提到GIL一般指的是Cpython,Cpython设置GIL的主要原因是为了保证不让多线程同时执行一条字节码,这就避免了可能多个线程对同一个对象进行操作;
# 保留GIL的原因:降低学习难度,移除GIL会降低Cpython单线程的执行效率;
(2)python统一进程内多线程的工作流程
①:拿到公共数据;
②:申请GIL;
③:Python解释器调用操作系统原生线程
④:cpu执行运算
⑤:当该线程执行一段时间消耗完,无论任务是否已经执行完毕,都会释放GIL
⑥:下一个被CPU调度的线程重复上面的过程;