【问题标题】:"Fire and forget" a process from a Python scriptPython 脚本中的“一劳永逸”过程
【发布时间】:2013-07-30 00:32:58
【问题描述】:

如何从 Python 脚本启动一个进程(例如,另一个 Python 脚本),以便“子”进程与“父”进程完全分离,这样父进程就可以 a) 继续愉快地进行而不用等待child to finish and b) 可以在不终止子进程的情况下终止吗?

家长:

import os

print "Parent started"
os.system("./child.py")
print "Parent finished"

孩子:

import time

print "Child started"
time.sleep(10)
print "Child finished"

运行parent.py 打印:

Parent started
Child started
Child finished
Parent finished

我想要它打印的内容:

Parent started
Child started
Parent finished
(seconds later)
Child finished

【问题讨论】:

  • 查看subprocess 模块,特别是subprocess.Popen(...).pid
  • @sberry:很好,看起来就像使用subprocess.Popen("./child.py") 完全符合我的需要,尽管从文档中并不清楚。如果您将评论添加为答案,我将很乐意接受,谢谢。
  • 根据平台的不同,您可能可以在 shell 命令的末尾添加一个&,它也可以工作。
  • @kevinsa5:是的,效果更好——我刚刚发现使用 Popen(),如果父进程被杀死,子进程似乎也被杀死了。
  • 请注意,添加& works on Linux,但does not work on Windows:您必须使用start

标签: python linux subprocess


【解决方案1】:

既然你提到了os.system,我认为值得一提的是,你应该使用os.spawn* 和模式P_NOWAIT 来实现“忘记”部分。

但是subprocess 模块提供了os.systemos,spawn* 等的替代品,所以你应该像这样使用它

import subprocess
p = subprocess.Popen("./child.py")
print "pid = ", p.pid

Replacing os.spawn with subprocess.Popen

正如我在 cmets 中解释的那样,两个进程 parent.pychild.py 仍然在同一个进程组上,因此终端会将信号(如 Ctrl-C)转发到前台进程组中的所有进程,因此两者都会得到当你Ctrl-C时被杀。因此,如果您不希望这样,您可以强制 child.py 使用以下内容进入新进程组:

#!/usr/bin/env python
import subprocess
import time
import os
p = subprocess.Popen("./child.py", preexec_fn=os.setsid)
print "pid = ", p.pid
time.sleep(30) # Ctrl-C at this point will not kill child.py
print "parent exit"

【讨论】:

  • 嘿,感谢您回答这个老问题。 subprocess.Popen 有一个问题:如果父进程终止 - 子进程也将终止。
  • 什么意思?我尝试了这段代码,至少如果父母死于自然原因,孩子不会被终止。 Ctrl-C(所有信号)不同,终端接收信号并将其转发到前台进程组中的任何进程。这里的进程组包括 parent.py 和 child.py 所以都被杀死了。我将尝试提供特定的 Popen 序列以在新进程组中创建子进程。
  • 看起来 preexec_fnPOSIX only。这是how to do it on Windows
【解决方案2】:

使用asyncio你可以写一个简单的装饰器为@background

import asyncio
import time

def background(f):
    def wrapped(*args, **kwargs):
        return asyncio.get_event_loop().run_in_executor(None, f, *args, *kwargs)

    return wrapped

@background
def foo():
    time.sleep(1)
    print("foo() completed")

print("Hello")
foo()
print("I didn't wait for foo()")

生产

>>> Hello
>>> I didn't wait for foo()
>>> foo() completed

【讨论】:

    【解决方案3】:

    回答我自己的问题:我最终只是在命令末尾使用os.system& 作为suggested by @kevinsa。这允许父进程在不终止子进程的情况下终止。

    这里有一些代码:

    child.py

    #!/usr/bin/python
    
    import time
    print "Child started"
    time.sleep(10)
    print "Child finished"
    

    parent.py,使用 subprocess.Popen:

    #!/usr/bin/python
    
    import subprocess
    import time
    
    print "Parent started"
    subprocess.Popen("./child.py")
    print "(child started, sleeping)"
    
    time.sleep(5)
    
    print "Parent finished"
    

    输出:

    $ ./parent.py
    Parent started
    (child started, sleeping)
    Child started
    ^CTraceback (most recent call last):
    Traceback (most recent call last):
      File "./child.py", line 5, in <module>
      File "./parent.py", line 13, in <module>
            time.sleep(10)
    time.sleep(5)
    KeyboardInterrupt
    KeyboardInterrupt
    
    • 注意如果父级被 Ctrl-C 打断,子级永远不会完成

    parent.py,使用 os.system 和 &

    #!/usr/bin/python
    
    import os
    import time
    
    print "Parent started"
    os.system("./child.py &")
    print "(child started, sleeping)"
    
    time.sleep(5)
    
    print "Parent finished"
    

    输出:

    $ ./parent.py
    Parent started
    (child started, sleeping)
    Child started
    ^CTraceback (most recent call last):
      File "./parent.py", line 12, in <module>
        time.sleep(5)
    KeyboardInterrupt
    
    $ Child finished
    

    注意孩子在 Ctrl-C 之外的生活。

    【讨论】:

    猜你喜欢
    • 2016-09-13
    • 1970-01-01
    • 2016-07-20
    • 2017-06-16
    • 1970-01-01
    • 2016-11-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多