有时运行单个单元需要很长时间,而它正在运行,我想在同一个笔记本中编写并运行其他单元,在同一个上下文中访问变量。
是否有可以使用的ipython魔法,当它被添加到单元格时,运行单元格会自动创建一个新线程并在笔记本中使用共享的全局数据运行?
答案 0 :(得分:10)
这可能不是一个答案,而是它的方向。我没有看到类似的东西,我也对此感兴趣。
我目前的调查结果表明,需要定义它自定义单元格魔法。好的参考资料将是文档中的自定义单元格魔术部分以及我将考虑的两个示例:
这些链接将代码包装在一个线程中。这可能是一个起点。
更新: github上的ngcm-tutorial描述了后台作业类
##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()
更新2:另一种选择:
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()
答案 1 :(得分:4)
这是我提出的一个小片段
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
它使用IPython内置模块IPython.lib.backgroundjobs
。所以代码小而简单,没有引入新的依赖。
我这样用:
jobs = jobs_manager()
%job [fetch_url(_) for _ in urls] # saves html file to disk
Starting job # 0 in a separate thread.
然后你可以用以下方式监控状态:
jobs.status()
Running jobs:
1 : [fetch_url(_) for _ in urls]
Dead jobs:
0 : [fetch_url(_) for _ in urls]
如果作业失败,您可以使用
检查堆栈跟踪jobs.traceback(0)
没有办法杀死一份工作。所以我小心翼翼地使用这个肮脏的黑客:
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')
它在给定的线程中引发SystemError
。所以要杀死我做的工作
kill_thread(jobs.all[1])
要杀死所有正在运行的作业
for thread in jobs.running:
kill_thread(thread)
我喜欢将%job
用于基于窗口小部件的进度条https://github.com/alexanderkuk/log-progress,如下所示:
%job [fetch_url(_) for _ in log_progress(urls, every=1)]
http://g.recordit.co/iZJsJm8BOL.gif
甚至可以使用%job
代替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
此代码存在一些明显问题:
您无法在%job
中使用任意代码。例如,没有分配而不是打印。所以我将它用于将结果存储在硬盘上的例程
有时kill_thread
中的脏黑客不起作用。我认为这就是为什么IPython.lib.backgroundjobs
没有设计这个功能的原因。如果线程正在执行某些系统调用,例如sleep
或read
,则会忽略异常。
它使用线程。 Python有GIL,所以%job
不能用于一些带有python字节码的繁重计算