简体   繁体   English

Python:非阻塞+非解散过程

[英]Python: Non-Blocking + Non defunct process

I would like to create a parent process, which will create many child process. 我想创建一个父进程,它将创建许多子进程。 Since the parent process is responsible to create the child process, the parent process would not care the status of the childs. 由于父进程负责创建子进程,因此父进程不关心子进程的状态。

Since subprocess.call is blocking, it doesn't work. 由于subprocess.call是阻塞的,它不起作用。 Therefore I use subprocess.Popen to replace call. 因此我使用subprocess.Popen来替换call。 However Popen will generate zombie (defunct) process once the child terminate( Link ). 然而,一旦孩子终止( 链接 ),Popen将生成僵尸(已解散)进程。

Is there a way to solve this problem? 有没有办法解决这个问题?

Thanks in advance 提前致谢

There are a lot of ways to deal with this. 有很多方法可以解决这个问题。 The key point is that zombie / "defunct" processes exist so that the parent process can collect their statuses. 关键点在于存在僵尸/“失效”进程,以便父进程可以收集其状态。

  1. As the creator of the process, you can announce your intent to ignore the status. 作为流程的创建者,您可以宣布忽略状态的意图。 The POSIX method is to set the flag SA_NOCLDWAIT (using sigaction ). POSIX方法是设置标志SA_NOCLDWAIT (使用sigaction )。 This is a bit of a pain to do in Python; 这在Python中有点痛苦; but most Unix-like systems allow you to simply ignore SIGCHLD / SIGCLD (the spelling varies from one Unix-like system to another), which is easy to do in Python: 但大多数类Unix系统允许你简单地忽略SIGCHLD / SIGCLD (拼写从一个类Unix系统到另一个系统不同),这在Python中很容易做到:

    import signal

    signal.signal(signal.SIGCHLD, signal.SIG_IGN)

  2. Or, if this is not available for some reason or does not work on your system, you can use an old stand-by trick: don't just fork once, fork twice. 或者,如果由于某种原因这不可用或者在您的系统上不起作用,您可以使用旧的备用技巧:不要只分叉一次,分叉两次。 In the first child, fork a second child; 在第一个孩子,叉第二个孩子; in the second child, use execve (or similar) to run the desired program; 在第二个孩子中,使用execve (或类似的)来运行所需的程序; and then in the first child, exit (with _exit ). 然后在第一个孩子中,退出(使用_exit )。 In the original parent, use wait or waidpid or whatever the OS provides, and collect the status of the first child. 在原始父级中,使用waitwaidpid或OS提供的任何内容,并收集第一个子级的状态。

    The reason this works is that the second child has now become an "orphan" (its parent, the first child, died and was collected by your original process). 这样做的原因是第二个孩子现在变成了一个“孤儿”(它的父母,第一个孩子,死了,并且是由你的原始过程收集的)。 As an orphan it is handed over to a proxy parent (specifically, to "init") which is always wait -ing and hence collects all the zombies right away. 作为孤儿,它被移交给代理父母(具体地说,“init”),它总是在wait ,因此立即收集所有的僵尸。

  3. In addition to the double fork, you can make your sub-processes live in their own separate session and/or give up controlling terminal access ("daemonize", in Unix-y terms). 除了双分支之外,您还可以将子流程置于各自的单独会话中和/或放弃控制终端访问(“daemonize”,以Unix-y术语表示)。 (This is a bit messy and OS-dependent; I've coded it before but for some corporate code I don't have access to now.) (这有点乱,并且依赖于操作系统;我之前编写过它,但对于一些我现在无法访问的公司代码。)

  4. Finally, you could simply collect those processes periodically. 最后,您可以定期收集这些流程。 If you're using the subprocess module, simply call the .poll function on each process, whenever it seems convenient. 如果您正在使用subprocess进程模块,只需在每个进程上调用.poll函数,只要看起来方便。 This will return None if the process is still running, and the exit status (having collected it) if it has finished. 如果进程仍在运行,则返回None如果已完成,则返回退出状态(已收集它)。 If some are still running, your main program can exit anyway while they keep running; 如果有些仍然在运行,那么你的主程序可以在它们继续运行的同时退出; at that point, they become orphaned, as in method #2 above. 在那时,他们变成了孤儿,就像上面的方法#2一样。

The "ignore SIGCHLD" method is simple and easy but has the drawback of interfering with library routines that create and wait-for sub-processes. “忽略SIGCHLD”方法简单易行,但缺点是干扰创建和等待子进程的库例程。 There's a work-around in Python 2.7 and later ( http://bugs.python.org/issue15756 ) but it means the library routines can't see any failures in those sub-processes. Python 2.7及更高版本( http://bugs.python.org/issue15756 )中有一个解决方法,但这意味着库例程无法在这些子流程中看到任何失败。

[Edit: http://bugs.python.org/issue1731717 is for p.wait() , where p is a process from subprocess.Popen ; [编辑: http://bugs.python.org/issue1731717p.wait()其中, p是从一个过程subprocess.Popen ; 15756 is specifically for p.poll() ; 15756专门用于p.poll() ; but in any case if you don't have the fixes, you have to resort to methods 2, 3, or 4.] 但无论如何,如果你没有修复,你必须求助于方法2,3或4.]

After terminating or killing a process the operating system waits for the parent process to collect the child process status. 终止或终止进程后,操作系统等待父进程收集子进程状态。 You can use the process' communicate() method to collect the status: 您可以使用进程'communic()方法来收集状态:

p = subprocess.Popen( ... )
p.terminate()
p.communicate()

Note that terminating a process allows the process to intercept the terminate signal and do whatever it wants to do with it. 请注意,终止进程允许进程拦截终止信号并执行它想要做的任何事情。 This is crucial since p.communicate() is a blocking call. 这是至关重要的,因为p.communicate()是一个阻塞调用。

If you do not wish this behavior use p.kill() instead of p.terminate() which lets the process not intercept the signal. 如果你不希望这种行为使用p.kill()而不是p.terminate(),它允许进程不拦截信号。

If you want to use p.terminate() and be sure the process ended itself you can use the psutil module to check on the process status. 如果要使用p.terminate()并确保进程自行结束,则可以使用psutil模块检查进程状态。

Please look at http://docs.python.org/2/library/multiprocessing.html 请查看http://docs.python.org/2/library/multiprocessing.html

It provides an API that is very similar to threads. 它提供了一个与线程非常相似的API。 You can wait for the child process to exit, if you want. 如果需要,您可以等待子进程退出。

torek's methods is ok! torek的方法还可以!

I found another way to deal with defunct process; 我找到了另一种处理已解散过程的方法;

we can use waitpid to recycle defunct process as needed: 我们可以根据需要使用waitpid来回收废弃的进程:

import os, subprocess, time

def recycle_pid():
    while True:
        try:
            pid, status, _ = os.wait3(os.WNOHANG)
            if pid == 0:
                break
            print("----- child %d terminated with status: %d" %(pid, status))
        except OSError,e:
            break

print("+++++ start pid:", subprocess.Popen("ls").pid)
recycle_pid()
print("+++++ start pid:", subprocess.Popen("ls").pid)
recycle_pid()
time.sleep(1)
recycle_pid()

recycle_pid is non-blocking, can call as needed. recycle_pid是非阻塞的,可以根据需要调用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM