2013-04-17 3 views
29

Sto tentando di eseguire una chiamata di sottoprocesso non bloccante per eseguire uno script slave.py dal mio programma main.py. Ho bisogno di passare arg da main.py a slave.py una volta quando (slave.py) viene prima avviato tramite subprocess.call dopo che questo slave.py viene eseguito per un periodo di tempo quindi esce.Sottoprocesso non bloccante

main.py 
for insert, (list) in enumerate(list, start =1): 

    sys.args = [list] 
    subprocess.call(["python", "slave.py", sys.args], shell = True) 


{loop through program and do more stuff..} 

E il mio script schiavo

slave.py 
print sys.args 
while True: 
    {do stuff with args in loop till finished} 
    time.sleep(30) 

Attualmente, blocchi slave.py main.py esecuzione il resto dei suoi compiti, voglio semplicemente slave.py essere indipendente main.py, una volta Ho passato discussioni ad esso. I due script non hanno più bisogno di comunicare.

Ho trovato alcuni post sulla rete non bloccanti subprocess.call, ma la maggior parte di essi sono centrati sul richiedere la comunicazione con slave.py in un punto che attualmente non ho bisogno. Qualcuno saprebbe come implementarlo in modo semplice ...?

risposta

32

È necessario utilizzare subprocess.Popen anziché subprocess.call.

Qualcosa di simile:

subprocess.Popen(["python", "slave.py"] + sys.argv[1:]) 

Dal docs on subprocess.call:

Eseguire il comando descritto da args. Attendere il completamento del comando, quindi restituire l'attributo returncode.

(Inoltre, non utilizzare un elenco per passare gli argomenti se si intende utilizzare shell = True).


Ecco un MCVE esempio che dimostra una non-blocco di chiamata suprocess:

import subprocess 
import time 

p = subprocess.Popen(['sleep', '5']) 

while p.poll() is None: 
    print('Still sleeping') 
    time.sleep(1) 

print('Not sleeping any longer. Exited with returncode %d' % p.returncode) 

Un approccio alternativo che si basa su cambiamenti più recenti per il linguaggio Python per consentire la co-routine di base parallelismo è:

# python3.5 required but could be modified to work with python3.4. 
import asyncio 

async def do_subprocess(): 
    print('Subprocess sleeping') 
    proc = await asyncio.create_subprocess_exec('sleep', '5') 
    returncode = await proc.wait() 
    print('Subprocess done sleeping. Return code = %d' % returncode) 

async def sleep_report(number): 
    for i in range(number + 1): 
     print('Slept for %d seconds' % i) 
     await asyncio.sleep(1) 

loop = asyncio.get_event_loop() 

tasks = [ 
    asyncio.ensure_future(do_subprocess()), 
    asyncio.ensure_future(sleep_report(5)), 
] 

loop.run_until_complete(asyncio.gather(*tasks)) 
loop.close() 

Testato su O S-X usando python2.7 & python3.6

+3

Grazie, sembra funzionare, tuttavia quando includo un ciclo While in slave.py sembra rimanere bloccato e non eseguire nulla nel ciclo (anche con una funzione timer.sleep() ..? – DavidJB

+0

@mgilson: Can per favore condividi un esempio generale su come usarlo? Voglio dire come dovrebbe apparire il flusso di controllo quando lo si usa in un modo non bloccante. Lo apprezzo. – ViFI

+1

@ViFI - Certo, ho aggiunto un esempio di utilizzo 'Popen' in modo non bloccante. – mgilson

17

Qui ci sono tre livelli di completezza.

Come dice mgilson, se si scambia semplicemente subprocess.call per subprocess.Popen, mantenendo tutto il resto uguale, main.py non aspetterà che slave.py finisca prima di continuare. Questo può essere abbastanza da solo. Se ti interessa lo zombie processes in giro, dovresti salvare l'oggetto restituito da subprocess.Popen e in un secondo momento chiamare il suo metodo wait. (Gli zombi scompaiono automaticamente quando main.py viene chiuso, quindi questo è solo un problema serio se main.py viene eseguito per un tempo molto lungo e/o potrebbe creare molti sottoprocessi.) E infine, se non vuoi uno zombie ma non si vuole decidere dove effettuare l'attesa (questo potrebbe essere appropriato se entrambi i processi vengono eseguiti per un tempo lungo e imprevedibile in seguito), utilizzare la libreria python-daemon per fare in modo che lo slave si dissoci dal master - in tal caso puoi continuare a utilizzare subprocess.call nel master.