我们知道,在 Python 里面可以使用 time.sleep 来让代码暂停一段时间,例如:

importtime

print( '...部分代码...')

time.sleep( 5)

print( '...剩下的代码...')

程序首先打印出 ...部分代码... ,然后等待5秒钟,再打印出 ...剩下的代码... 。

现在大家想一想,有没有什么办法,在不使用 time.sleep 的情况下,让程序暂停5秒?

你可能会说,用 requests 访问一个延迟5秒的网址、或者用递归版算法计算斐波那契数列第36位……这些奇技淫巧。

不过今天我说的,是另外一个东西, threading 模块里面的 Event 。

我们来看看它的用法:

importthreading
event = threading.Event
print( '...部分代码...')
event.wait( 5)
print( '...剩下的代码...')

这样一来,程序首先打印出 ...部分代码... ,然后等待5秒钟,再打印出 ...剩下的代码... 。

功能看起来跟 time.sleep 没什么区别,那为什么我要特别提到它呢?因为在多线程里面,它比 time.sleep 更有用。我们来看一个例子:

importthreading
classChecker(threading.Thread):
def__init__(self, event):
super.__init__
self.event = event
defrun(self):
whilenotself.event.is_set:
print( '检查 redis 是否有数据')
time.sleep( 60)
trigger_async_task
event = threading.Event
checker = Checker(event)
checker.start
ifuser_cancel_task:
event.set

我来解释一下这段代码的意思。在主线程里面,我调用 trigger_async_task 触发了一个异步任务。这个任务多久完成我并不清楚。但是这个任务完成以后,它会往 Redis 里面发送一条消息,只要 Redis 有这个消息了,我就知道它完成了。所以我要创建一个 checker 子线程,每60秒去 Redis里面检查任务是否完成。如果没有完成,就暂停60秒,然后再检查。

但某些情况下,我不需要等待了,例如用户主动取消了任务。这个时候,我就想提前结束这个 checker 子线程。

但是我们知道,线程是不能从外面主动杀死的,只能让它自己退出。所以当我执行 event.set 后,子线程里面 self.event.is_set 就会返回 False,于是这个循环就不会继续执行了。

可是,如果某一轮循环刚刚开始,我在主线程里面调用了 event.set 。此时,子线程还在 time.sleep 中,那么子线程需要等待60秒才会退出。

但如果我修改一下代码,使用 self.event.wait(60) :

importthreading
classChecker(threading.Thread):
def__init__(self, event):
super.__init__
self.event = event
defrun(self):
whilenotself.event.is_set:
print( '检查 redis 是否有数据')
self.event.wait( 60)
trigger_task
event = threading.Event
checker = Checker(event)
checker.start
ifuser_cancel_task:
event.set

那么,即便 self.event.wait(60) 刚刚开始阻塞,只要我在主线程中执行了 event.set ,子线程里面的阻塞立刻就会结束。于是子线程立刻就会结束。不需要再白白等待60秒。

并且, event.wait 这个函数在底层是使用 C 语言实现的,不受 GIL 锁的干扰。

总结一下简单来说就是:如果你想在多线程时更灵活地控制代码运行等待,那么用 event.wait 要比 time.sleep 更合适。

作者:kingname