2015-08-18 4 views
18

Manchmal dauert es eine lange Zeit, eine einzelne Zelle auszuführen, während es ausgeführt wird, möchte ich andere Zellen in demselben Notizbuch schreiben und ausführen, Zugriff auf die Variablen in der gleiche Kontext.ein neuer Thread zum Ausführen einer Zelle in Ipython/Jupyter Notebook

Gibt es irgendeine ipython Magie, die verwendet werden kann, so dass, wenn es zu einer Zelle hinzugefügt wird, das Ausführen der Zelle automatisch einen neuen Thread erstellt und mit freigegebenen globalen Daten im Notizbuch ausgeführt wird?

Antwort

7

Es kann nicht eine Antwort sein, aber eher die Richtung dazu. So etwas habe ich noch nicht gesehen, trotzdem interessiert mich das auch.

Meine aktuellen Ergebnisse, die darauf hindeuten, dass man es eigene benutzerdefinierte Zelle magischen definieren muss. Gute Referenzen wäre die benutzerdefinierte Zelle Magie Abschnitt in der Dokumentation und zwei Beispiele, die ich in Betracht ziehen würden:

Beide Links, die den Code in einem Thread enthalten. Das könnte ein Ausgangspunkt sein.

UPDATE: ngcm-Tutorial auf GitHub hat Beschreibung von Hintergrundjobs Klasse

##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() 

UPDATE 2: Eine weitere Option:

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() 
2

Hier ist ein kleiner Ausschnitt, den ich kam mit

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 

Es verwendet das integrierte IPython-Modul IPython.lib.backgroundjobs. Der Code ist also klein und einfach und es werden keine neuen Abhängigkeiten eingeführt.

Ich benutze es wie folgt aus:

jobs = jobs_manager() 

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

Dann Sie den Zustand mit überwachen können:

jobs.status() 

Running jobs: 
1 : [fetch_url(_) for _ in urls] 

Dead jobs: 
0 : [fetch_url(_) for _ in urls] 

Wenn Auftrag fehlschlägt Sie Stack-Trace mit

jobs.traceback(0) 

Es gibt inspizieren keine Möglichkeit, einen Job zu töten. So verwende ich sorgfältig mit diesem schmutzigen Hack:

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

Es wirft SystemError in einem bestimmten Thread.So einen Job zu töten ich

tun
kill_thread(jobs.all[1]) 

Um alle laufenden Aufträge zu töten ich

for thread in jobs.running: 
    kill_thread(thread) 

Ich mag verwenden %job mit Widget-basierte Fortschrittsbalken https://github.com/alexanderkuk/log-progress wie folgt aus:

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

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

Man kann sogarverwenden 210 statt 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

Einige offensichtliche Probleme mit diesem Code:

  1. Sie nicht beliebigen Code in %job verwenden können. Es kann keine Zuweisungen geben und nicht zum Beispiel drucken. Also verwende ich es mit Routinen, die Ergebnisse auf der Festplatte speichern

  2. Manchmal funktioniert schmutzig Hack in kill_thread nicht. Ich denke, das ist, warum IPython.lib.backgroundjobs diese Funktionalität nicht von Entwurf hat. Wenn der Thread einen Systemaufruf wie sleep oder read ausführt, wird die Ausnahme ignoriert.

  3. Es verwendet Threads. Python hat GIL, so %job kann nicht für einige schwere Berechnungen verwendet werden, die

+0

@minrk in Python-Bytecode nehmen Könnten Sie an dieser Antwort suchen Sie und sehen, ob es noch andere Probleme mit dieser Lösung, dass ich nicht glaube, Über? – alexanderkuk