Python的线程21 Barrier类再次解析

Posted 雷学委

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Python的线程21 Barrier类再次解析相关的知识,希望对你有一定的参考价值。

正式的Python专栏第58篇,同学站住,别错过这个从0开始的文章!

这篇继续介绍threading库里面的类,threading.Barrier类。

这个类很好用,直接指定数值,通过调用其wait方法,就轻松实现多个线程同步等待

大家也看到学委之前用condition的实现(需要正确的使用notify和wait,还有加上wait(timeout) 来实现十个运动员等待)明显比Barrier类绕了一些。

继续看看Barrier类

再次阅读一下Barrier类的构造方法(如下),我们看到,它内部持有了Condition对象和_count, _parties。

基本上Barrier利用了这三个实现了一个等待释放的机制,下面看看wait源码。

另外,还有其他参数像action,这个会再整个Barrier被release之前由某一个线程调用。

def __init__(self, parties, action=None, timeout=None):
    self._cond = Condition(Lock())
    self._action = action
    self._timeout = timeout
    self._parties = parties
    self._state = 0 #0 filling, 1, draining, -1 resetting, -2 broken
    self._count = 0

学委准备了下面这个小demo方法,读者可以在前面分享的代码中贴上。

def post_action():
    print("运动员调用了barrier释放后的方法- %s" % threading.current_thread().name)
barrier = threading.Barrier(parties=10,action=post_action)

重新运行代码可以看到,只有某一个运动员线程(哪个运动员是随机的,看实际项目中是那个线程最后调用wait方法的)执行了post_aciton方法。

下面是某一次执行的效果展示:

重点是这个wait方法

它像Condition一样也支持timeout(也就是线程数量凑不够,在一个固定等待延迟后强制执行wait后面的代码)。

def wait(self, timeout=None):
   if timeout is None:
       timeout = self._timeout
   with self._cond:
       self._enter() # Block while the barrier drains.
       index = self._count
       self._count += 1
       try:
           if index + 1 == self._parties:
               # We release the barrier
               self._release()
           else:
               # We wait until someone releases us
               self._wait(timeout)
           return index
       finally:
           self._count -= 1
           # Wake up any threads waiting for barrier to drain.
           self._exit()

这段代码也非常直观,每一个线程调用一次wait方法,Barrier对象持有的_count自增1(从0开始)。

知道_count 为_parties-1的时候,Barrier调用_release释放内部持有的锁。

好,继续看一下_release 函数,这个更加简单了,重点我们看到:self._cond.notify_all(),这句执行了。

它调用了notify_all函数,释放了其他调用的condition.wait方法。

这就是Barrier类的内部实现。

def _release(self):
    try:
        if self._action:
            self._action()
        # enter draining state
        self._state = 1
        self._cond.notify_all()
    except:
        #an exception during the _action handler.  Break and reraise
        self._break()
        raise

另外,这里我们也看到_action方法被调用了,这也验证了学委上面贴上的demo
运行图,10号运动员打印了post_action方法。

补充一下with方式的使用Condition,也可以应用在Lock/Rlock对象。

像下面的代码:

with xuewei_lock:
    do_something()

等效于:

xuewei_lock.acquire()
do_something()
xuewei_lock.release()

这种风格让这个锁的使用看起来更加优雅,也不容易漏了。

毕竟有时候方法不小心写多了,后面容易就给忘记了,特别是多人协作的项目上。(后来者非常有可能在项目非常紧急的情况下,修改代码导致某些情况锁没有被释放,产生系统故障,这就非常可惜。)

总结

Barrier类内部使用了Condition这个资源控制单元来实现,加上一个阀值和计数器变量,实现多线程‘屏障’功能。

其他方法读者可以自行阅读,加深对这个类的理解。

喜欢Python的朋友,请关注学委的 Python基础专栏 or Python入门到精通大专栏

持续学习持续开发,我是雷学委!
编程很有趣,关键是把技术搞透彻讲明白。
欢迎关注微信,点赞支持收藏!

以上是关于Python的线程21 Barrier类再次解析的主要内容,如果未能解决你的问题,请参考以下文章

Python的线程21 Barrier类再次解析

Python的线程21 Barrier类再次解析

Python的线程20 强迫症患者之Barrier类

Python的线程20 强迫症患者之Barrier类

Python的线程20 强迫症患者之Barrier类

Python的线程20 强迫症患者之Barrier类