线程和进程混合的奇怪行为

时间:2016-09-15 20:57:03

标签: python multithreading multiprocessing

我正在运行以下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

3 个答案:

答案 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函数的方法。在你的情况下,那是mainmain在您启动后台线程后立即返回,即使进程没有退出,因为仍然有一个非守护程序线程正在运行。

然而,一旦执行命中os._exit(code),子进程就会被终止,无论任何非守护进程线程仍在执行。