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调度的线程重复上面的过程;

 

posted @ 2018-11-01 19:28  soulgou  阅读(196)  评论(0编辑  收藏  举报