多线程中的Condition对象

2020年6月23日 / 36次阅读 / Last Modified 2020年6月23日
多线程

python多线程模块threading中,有一个Condition对象。这个对象可以用来控制更加复杂的线程间的同步。

Condition本身包含一个Lock或者Rlock,可以在创建Congdition对象的时候传一个进去,如果不传,默认创建一个Rlock。通过with语句,即可acquire,并且自动release。不过这不是Condition对象的用法,它的亮点在于wait和notify。

在通过with语句获取lock之后,线程可以通过wait,release这个lock,并且block。或者线程可以通过notify,让渡(relinquish)自己的lock给在这个condition对象上wait的线程用。

Note: the notify() and notify_all() methods don’t release the lock; this means that the thread or threads awakened will not return from their wait() call immediately, but only when the thread that called notify() or notify_all() finally relinquishes ownership of the lock.

notify之后,wait在那个condition上的thread,不会离开从wait返回,而是等待调用notify的thread执行完它的代码逻辑,relinquish ownership of the lock。即明确指示wait在那个condition上的thread执行(用notify_all,就需要在一个范围内随机调度)。好性感的操控!

from time import sleep
from threading import Thread, Lock, Condition

mutex = Lock()
conv = Condition(mutex)

def target1():
    with conv:
        print(1)
        sleep(2)
        conv.notify()
        conv.wait()
        print(3)
        sleep(2)
        conv.notify()
        conv.wait()
        print(5)
        sleep(2)
        conv.notify()
        conv.wait()
        print(7)
        conv.notify()


def target2():
    with conv:
        print(2)
        sleep(2)
        conv.notify()
        conv.wait()
        print(4)
        sleep(2)
        conv.notify()
        conv.wait()
        print(6)
        sleep(2)
        conv.notify()
        conv.wait()
        print(8)        
        
        
thread_01 = Thread(target=target1, args=())
thread_02 = Thread(target=target2, args=())
thread_01.start()
thread_02.start()

这段代码执行的时候,会连续打印1-8,这就是两个线程间同步的更加精准的控制手段。两个线程都是在wait的时候释放lock,此时notify已经发出,那个wait的线程继续执行。

学习到这里,我有个疑问,如果两个线程这么来来回回的传递lock,第3个线程会不会饿死?于是,我写了下面的测试代码。

from time import sleep
from threading import Thread, Lock, Condition

mutex = Lock()
conv = Condition(mutex)

def target1():
    with conv:
        print(1)
        sleep(2)
        conv.notify()
        conv.wait()
        print(3)
        sleep(2)
        conv.notify()
        conv.wait()
        print(5)
        sleep(2)
        conv.notify()
        conv.wait()
        print(7)
        conv.notify()

def target2():
    with conv:
        print(2)
        sleep(2)
        conv.notify()
        conv.wait()
        print(4)
        sleep(2)
        conv.notify()
        conv.wait()
        print(6)
        sleep(2)
        conv.notify()
        conv.wait()
        print(8)        
        
def target3():
    for i in range(4):
        with mutex:
            print('what...')
        sleep(2)

thread_01 = Thread(target=target1, args=())
thread_02 = Thread(target=target2, args=())
thread_03 = Thread(target=target3, args=())
thread_01.start()
thread_02.start()
thread_03.start()

看来,线程饿死的情况是不会发生的,python线程间的调度策略,依然会保证上面代码中target3的调度。从测试情况看,线程target3的调度有随机性,但是target1和target2相互间的执行顺序是严格控制的。

不过,个人感觉使用Condition对象操作线程间的执行,还是很容易出现dead wait的情况。多线程代码还是需要精心调试的,比如上面的测试代码,如果把3个线程的start顺序交换一下,就会出现dead wait:

thread_02.start()
thread_01.start()
thread_03.start()

先打印2,然后1......,最后打印出8后,代码没有notify,target1线程就出现了dead wait。

最后,with conv这行代码,其实效果跟wait很相似,某一个线程wait后,另一个线程才能执行完with conv这行代码,进入内部block执行。

在多线程编程时,越是强大的东西,代码越要小心...

-- EOF --

本文链接:https://www.pynote.net/archives/2078

留言区

《多线程中的Condition对象》有2条留言

电子邮件地址不会被公开。 必填项已用*标注

  • 麦新杰

    Condition对象的wait,只能用notify来唤醒! [回复]

  • 麦新杰

    python queue模块内就有Condition对象的使用。 [回复]


前一篇:
后一篇:

More


©Copyright 麦新杰 Since 2019 Python笔记

go to top