α2α2 type and multithreading ipython ipython-notebook jupyter

multithreading - type - jupyter latex



un nuevo hilo para ejecutar una celda en el portátil ipython/jupyter (2)

Aquí hay un pequeño fragmento que se me ocurrió

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

Utiliza el módulo IPython.lib.backgroundjobs IPython IPython.lib.backgroundjobs . Así que el código es pequeño y simple y no se introducen nuevas dependencias.

Lo uso así:

jobs = jobs_manager() %job [fetch_url(_) for _ in urls] # saves html file to disk Starting job # 0 in a separate thread.

Luego puedes monitorear el estado con:

jobs.status() Running jobs: 1 : [fetch_url(_) for _ in urls] Dead jobs: 0 : [fetch_url(_) for _ in urls]

Si el trabajo falla, puede inspeccionar el seguimiento de pila con

jobs.traceback(0)

No hay manera de matar un trabajo. Así que cuidadosamente uso este truco sucio:

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'')

Plantea SystemError en un hilo dado. Así que para matar un trabajo que hago

kill_thread(jobs.all[1])

Para matar a todos los trabajos en ejecución que hago

for thread in jobs.running: kill_thread(thread)

Me gusta usar %job con la barra de progreso basada en widgets https://github.com/alexanderkuk/log-progress como este:

%job [fetch_url(_) for _ in log_progress(urls, every=1)]

http://g.recordit.co/iZJsJm8BOL.gif

Incluso se puede usar %job lugar de multiprocessing.TreadPool . 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

Algunos problemas obvios con este código:

  1. No puede utilizar código arbitrario en %job . No puede haber asignaciones y no impresiones por ejemplo. Así que lo uso con rutinas que almacenan resultados en el disco duro.

  2. A veces, el truco sucio en kill_thread no funciona. Creo que es por eso que IPython.lib.backgroundjobs no tiene esta funcionalidad por diseño. Si el subproceso está haciendo alguna llamada al sistema, como la sleep o la excepción de read se ignora.

  3. Utiliza hilos. Python tiene GIL, por lo que no se puede usar %job para algunos cálculos pesados ​​que toman el código de byte de python

A veces lleva mucho tiempo ejecutar una sola celda, mientras se ejecuta, me gustaría escribir y ejecutar otras celdas en el mismo cuaderno, accediendo a las variables en el mismo contexto.

¿Existe alguna magia de ipython que pueda usarse de tal manera que cuando se agrega a una celda, la ejecución de la celda creará automáticamente un nuevo hilo y se ejecutará con datos globales compartidos en el cuaderno?


Puede que no sea una respuesta, sino la dirección de la misma. No vi nada de eso, todavía estoy interesado en esto también.

Mis hallazgos actuales sugieren que uno necesita definir su propia magia celular personalizada . Buenas referencias serían la sección personalizada de magia celular en la documentación y dos ejemplos que consideraría:

Ambos enlaces envuelven el código en un hilo. Ese podría ser un punto de partida.

ACTUALIZACIÓN: ngcm-tutorial at github tiene una descripción de la clase de trabajos en segundo plano

##github.com/jupyter/ngcm-tutorial/blob/master/Day-1/IPython%20Kernel/Background%20Jobs.ipynb from IPython.lib import backgroundjobs as bg jobs = bg.BackgroundJobManager() def printfunc(interval=1, reps=5): for n in range(reps): time.sleep(interval) print(''In the background... %i'' % n) sys.stdout.flush() print(''All done!'') sys.stdout.flush() jobs.new(''printfunc(1,3)'') jobs.status()

ACTUALIZACIÓN 2: Otra opción:

from IPython.display import display from ipywidgets import IntProgress import threading class App(object): def __init__(self, nloops=2000): self.nloops = nloops self.pb = IntProgress(description=''Thread loops'', min=0, max=self.nloops) def start(self): display(self.pb) while self.pb.value < self.nloops: self.pb.value += 1 self.pb.color = ''red'' app = App(nloops=20000) t = threading.Thread(target=app.start) t.start() #t.join()