Ecco un piccolo frammento che mi è venuta con
def jobs_manager():
from IPython.lib.backgroundjobs import BackgroundJobManager
from IPython.core.magic import register_line_magic
from IPython import get_ipython
jobs = BackgroundJobManager()
@register_line_magic
def job(line):
ip = get_ipython()
jobs.new(line, ip.user_global_ns)
return jobs
Utilizza il modulo incorporato IPython IPython.lib.backgroundjobs
. Quindi il codice è piccolo e semplice e non vengono introdotte nuove dipendenze.
lo uso così:
jobs = jobs_manager()
%job [fetch_url(_) for _ in urls] # saves html file to disk
Starting job # 0 in a separate thread.
Quindi è possibile monitorare lo stato con:
jobs.status()
Running jobs:
1 : [fetch_url(_) for _ in urls]
Dead jobs:
0 : [fetch_url(_) for _ in urls]
Se il lavoro non è possibile ispezionare analisi dello stack con
jobs.traceback(0)
C'è nessun modo per uccidere un lavoro. Quindi io uso con attenzione questo hack sporco:
def kill_thread(thread):
import ctypes
id = thread.ident
code = ctypes.pythonapi.PyThreadState_SetAsyncExc(
ctypes.c_long(id),
ctypes.py_object(SystemError)
)
if code == 0:
raise ValueError('invalid thread id')
elif code != 1:
ctypes.pythonapi.PyThreadState_SetAsyncExc(
ctypes.c_long(id),
ctypes.c_long(0)
)
raise SystemError('PyThreadState_SetAsyncExc failed')
solleva SystemError
in un dato thread.Quindi, per uccidere un lavoro che faccio
kill_thread(jobs.all[1])
Per uccidere tutti i processi in esecuzione che faccio
for thread in jobs.running:
kill_thread(thread)
Mi piace usare %job
con basata su widget barra di avanzamento https://github.com/alexanderkuk/log-progress come questo:
%job [fetch_url(_) for _ in log_progress(urls, every=1)]
http://g.recordit.co/iZJsJm8BOL.gif
Si può persino usare %job
invece di multiprocessing.TreadPool
:
for chunk in get_chunks(urls, 3):
%job [fetch_url(_) for _ in log_progress(chunk, every=1)]
http://g.recordit.co/oTVCwugZYk.gif
Alcuni problemi evidenti con questo codice:
Non è possibile utilizzare codice arbitrario nel %job
. Non ci possono essere assegnazioni e non stampe per esempio. Quindi lo uso con le routine che memorizzano i risultati sul disco rigido
A volte il trucco sporco in kill_thread
non funziona. Penso che sia per questo che IPython.lib.backgroundjobs
non ha questa funzionalità di progettazione. Se thread sta eseguendo alcune chiamate di sistema come sleep
o read
, l'eccezione viene ignorata.
Utilizza thread. Python ha GIL, in modo da %job
non può essere utilizzato per alcuni calcoli pesanti che prendono in bytecode Python
@minrk La prego di guardare a questa risposta e vedere se ci eventuali altri problemi con questa soluzione che io non pensavo di? – alexanderkuk