Задействайте и забравете процес от скрипт на Python

Как да стартирам процес (например друг скрипт на Python) от скрипт на Python, така че „детето“ процесът да е напълно отделен от „родителя“, така че родителят да може а) да продължи по своя весел начин, без да чака детето да завърши и б) могат да бъдат прекратени без прекратяване на дъщерния процес?

Родител:

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

person Sergey    schedule 30.07.2013    source източник
comment
Вижте модула subprocess и по-специално subprocess.Popen(...).pid   -  person sberry    schedule 30.07.2013
comment
@sberry: Хубаво, изглежда, че само използването на subprocess.Popen("./child.py") прави точно това, от което се нуждая, въпреки че изобщо не е ясно от документите. Ако добавите коментара си като отговор, ще се радвам да го приема, благодаря.   -  person Sergey    schedule 30.07.2013
comment
В зависимост от платформата вероятно бихте могли да добавите & в края на командата на обвивката и тя също ще работи.   -  person kevinsa5    schedule 30.07.2013
comment
@kevinsa5: да, това работи още по-добре - току-що открих, че с Popen(), ако родителският процес бъде убит, детето също изглежда е убито.   -  person Sergey    schedule 30.07.2013


Отговори (3)


Тъй като споменахте os.system, мисля, че си струва да споменете, че трябваше да използвате os.spawn* с режим P_NOWAIT, за да постигнете частта „забравяне“.

Но модулът subprocess осигурява заместители за os.system, os,spawn* и т.н., така че трябва да използвате това вместо това

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

Вижте Замяна на os.spawn с subprocess.Popen

Както обясних в коментарите, и двата процеса parent.py и child.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"
person RubenLaguna    schedule 10.06.2014
comment
Хей, благодаря за отговора на този стар въпрос. Има обаче проблем с subprocess.Popen: ако родителският процес е прекратен - детето също се прекратява. - person Sergey; 11.06.2014
comment
Какво имаш предвид? Опитах тази част от кода и детето не беше прекратено поне ако родителят умре от естествена смърт. Ctrl-C (всички сигнали) са различни, терминалът получава сигнала и го препраща към всеки процес в групата процеси на преден план. Групата процеси тук включва parent.py и child.py, така че и двата се убиват. Ще се опитам да осигуря конкретната последователност на Popen за създаване на дете в нова група процеси. - person RubenLaguna; 11.06.2014

Използвайки 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
person nehem    schedule 12.11.2018

Отговарям на собствения си въпрос: В крайна сметка просто използвах os.system с & в края на командата, както беше предложено от @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.

person Sergey    schedule 10.06.2014