Python学习总结笔记(5)--进程/线程Event机制使用时要注意的一个问题

时间:2022-04-14 06:53:09

Python多进程/线程使用Event对象需要注意对象定义的形式,否则可能造成“死锁”。在实际使用过程中发现的一个问题,调试好久没找到原因,为了避免大家走弯路,这里分享下。

0x01 “死锁”产生

threading.Event对象提供了一个进程/线程间协调运行的机制。网上讲的文章很多,这里就不多说,先看一个例子:

#/usr/bin/env python
#coding:utf-8

__author__ = 'kikay'

import multiprocessing
import time

#定义Event全局对象
e=multiprocessing.Event()

#扩展的进程类
class MyProcessEx(multiprocessing.Process):
def __init__(self,name):
super(MyProcessEx,self).__init__(name=name)

def run(self):
global e
print 'process({name}) work...'.format(name=self.name)
time.sleep(1)
print 'process({name}) sleep...'.format(name=self.name)
#进入等待状态
e.wait()
print 'process({name}) awake...'.format(name=self.name)

if __name__=='__main__':
p=MyProcessEx(name='Model1')
p.start()
time.sleep(5)
#唤醒
e.set()
print '{name} is ending ...'.format(name=p.name)
print 'Event status:{s}'.format(s=e.is_set())
p.join()
print 'All Done!!!'

上面的代码很完整,好像没什么问题。我们期望的是子进程‘Model1’等待主进程在5s后“唤醒”。但是实际结果如下:

process(Model1) work...
process(Model1) sleep...
Model1 is ending ...
Event status:True

子线程将会一直“死锁”,也就是子线程并没有被“唤醒”,但是我们看Event对象实际处于“激活”状态,只是在子进程中没有响应。

0x02 解决办法

通过多次试验,发现这个问题的产生与Event的使用方式有关系,改写代码如下:

#/usr/bin/env python
#coding:utf-8

__author__ = 'kikay'

import multiprocessing
import time

#定义Event全局对象
e=multiprocessing.Event()

#扩展的进程类
class MyProcessEx(multiprocessing.Process):
def __init__(self,name,event):
super(MyProcessEx,self).__init__(name=name)
self.__event=event

def run(self):
global e
print 'process({name}) work...'.format(name=self.name)
time.sleep(1)
print 'process({name}) sleep...'.format(name=self.name)
#进入等待状态
self.__event.wait()
print 'process({name}) awake...'.format(name=self.name)

if __name__=='__main__':
p=MyProcessEx(name='Model1',event=e)
p.start()
time.sleep(5)
#唤醒
e.set()
print '{name} is ending ...'.format(name=p.name)
print 'Event status:{s}'.format(s=e.is_set())
p.join()
print 'All Done!!!'

运行结果:

process(Model1) work...
process(Model1) sleep...
process(Model1) awake...
Model1 is ending ...
Event status:True
All Done!!!

可见,修改后运行与我们期望的结果一致了。我们将子进程处理函数的Event对象作为一个参数传递进去,而不是采用全局变量的形式调用,问题就解决了。

0x03 后记

多线程Event也存在同样的问题,但是其他的Lock/RLock、Condition、Semaphore等进程/线程对象不存在这个问题。所以在实际应用过程中,我们要采用第二种方式处理Event对象来避免出现“死锁”。

(至于深层次的原因我现在还没有完全搞清楚,有时间研究下threading.Event实现代码,然后再共享出来。如果有大牛知道原因的,可以告诉我下,相互学习,共同提高!!!)