我正在运行以下python代码:
import threading
import multiprocessing
def forever_print():
while True:
print("")
def main():
t = threading.Thread(target=forever_print)
t.start()
return
if __name__=='__main__':
p = multiprocessing.Process(target=main)
p.start()
p.join()
print("main process on control")
终止。
当我从新进程中解包main
时,直接运行它,就像这样:
if name == '__main__':
main()
脚本永远存在,正如我认为应该的那样。假设t
是非守护进程,p
不应该在第一种情况下停止,我错了吗?
我基本上设置了这个小测试,因为我一直在开发一个应用程序,其中线程在子进程中生成,并且它已经显示出一些奇怪的行为(有时它会正常终止,有时它不会终止)。从广义上讲,我想我想知道的是,当混合这两个python库时,是否存在某种“陷阱”。
我的运行环境:python 2.7 @ Ubuntu 14.04 LTS
答案 0 :(得分:2)
您需要在t.join()
功能中致电main
。
当你的main
函数返回时,进程将以其两个线程终止。
p.join()
阻止等待生成进程结束的主线程。然后,您生成的进程会创建一个线程,但不会等待它结束。它会立即返回,从而破坏线程本身。
如果线程共享内存,则进程不会。因此,您在新生成的进程中创建的线程仍然会降级到该进程。父进程不知道它。
答案 1 :(得分:2)
现在,由 multiprocessing
工作进程创建的线程就像进程终止一样执行守护程序线程:工作进程退出而不等待它创建的线程终止。这是由于工作进程使用os._exit()
关闭,这会跳过大多数正常的关闭处理(特别是跳过sys.exit()
非守护进程的正常退出处理代码(.join()
) threading.Thread
S)。
最简单的解决方法是让工作进程显式.join()
他们创建的非守护程序线程。
有一个关于此行为的公开错误报告,但它没有取得多大进展:http://bugs.python.org/issue18966
答案 2 :(得分:2)
问题在于multiprocessing
机制在你的目标函数退出后调用os._exit()
,这会猛烈地杀死子进程,即使它有后台线程运行。
Process.start()
的代码如下所示:
def start(self):
'''
Start child process
'''
assert self._popen is None, 'cannot start a process twice'
assert self._parent_pid == os.getpid(), \
'can only start a process object created by current process'
assert not _current_process._daemonic, \
'daemonic processes are not allowed to have children'
_cleanup()
if self._Popen is not None:
Popen = self._Popen
else:
from .forking import Popen
self._popen = Popen(self)
_current_process._children.add(self)
Popen.__init__
看起来像这样:
def __init__(self, process_obj):
sys.stdout.flush()
sys.stderr.flush()
self.returncode = None
self.pid = os.fork() # This forks a new process
if self.pid == 0: # This if block runs in the new process
if 'random' in sys.modules:
import random
random.seed()
code = process_obj._bootstrap() # This calls your target function
sys.stdout.flush()
sys.stderr.flush()
os._exit(code) # Violent death of the child process happens here
_bootstrap方法实际上是执行传递给target
对象的Process
函数的方法。在你的情况下,那是main
。 main
在您启动后台线程后立即返回,即使进程没有退出,因为仍然有一个非守护程序线程正在运行。
然而,一旦执行命中os._exit(code)
,子进程就会被终止,无论任何非守护进程线程仍在执行。