Python大师我需要你的帮助。我遇到了很奇怪的行为: 空python进程挂起。看起来它会分配一些锁定的资源。
ENV:
问题描述:
1)我有一个带有线程的记录器来处理该线程的后台和队列中的消息。 Logger source code(稍微简化一点)。
2)我有一个简单的脚本,它使用我的记录器(只是代码来显示我的问题):
import os
from multiprocessing import Process
from my_logging import get_logger
def func():
pass
if __name__ == '__main__':
logger = get_logger(__name__)
logger.start()
for _ in range(2):
logger.info('message')
proc = Process(target=func)
proc.start()
proc.join(timeout=3)
print('TEST PROCESS JOINED: is_alive={0}'.format(proc.is_alive()))
logger.stop()
print('EXIT')
有时候这个测试脚本会挂起。脚本在加入进程“proc”时挂起(当脚本完成执行时)。测试过程“proc”保持活力。
要重现此问题,您可以循环运行脚本:
$ for i in {1..100} ; do /opt/python3.5.3/bin/python3.5 test.py ; done
调查:
Strace显示以下内容:
strace: Process 25273 attached
futex(0x2275550, FUTEX_WAIT_BITSET_PRIVATE|FUTEX_CLOCK_REALTIME, 0, NULL, ffffffff
我想出了进程挂起的地方。它挂在多处理模块,文件process.py,第269行(python3.5.3),在刷新STDERR:
...
267 util.info('process exiting with exitcode %d' % exitcode)
268 sys.stdout.flush()
269 sys.stderr.flush()
...
如果第269行注释,脚本总是成功完成。
我的想法:
默认情况下,logging.StreamHandler使用sys.stderr作为流。
如果在记录器将数据刷新到STDERR时已分叉进程,则进程上下文会获得一些锁定的资源,并在刷新STDERR时进一步挂起。
解决问题的一些解决方法:
您对此行为有什么想法吗?问题出在哪儿?我做错了吗?
答案 0 :(得分:1)
看起来此行为与此问题有关:http://bugs.python.org/issue6721
答案 1 :(得分:0)
问题:有时......测试过程“proc”保持活力。
我只能重现你的
TEST PROCESS:0 JOINED: is_alive=True
向
time.sleep(5)
添加def func():
您使用proc.join(timeout=3)
,这是预期的行为。<强>结论强>:
超载您的系统,在我的环境中启动 30个正在运行的进程,触发您的proc.join(timeout=3)
。 您可以重新考虑 Testcase 以重现您的问题。我认为,一种方法是使用
Process/Thread
对time.sleep(0.05)
进行微调以发布时间片。
您正在使用from multiprocessing import Queue
请改用from queue import Queue
。
来自文档
类多处理.Queue
用于多处理(而非多线程)上下文的队列类。
在class QueueHandler(logging.Handler):
中,阻止
self.queue.put_nowait(record)
后
class QueueListener(object): ... def stop(self): ...
实现,例如
class QueueHandler(logging.Handler): def __init__(self): self.stop = Event() ...
在def _monitor(self):
中仅使用 ONE while ...
循环。
等到self._thread
停止
class QueueListener(object): ... def stop(self): self.handler.stop.set() while not self.queue.empty(): time.sleep(0.5) # Don't use double flags #self._stop.set() self.queue.put_nowait(self._sentinel) self._thread.join()