python - 为什么使用 IPython 魔术 %reset 清除变量空间时 pool.join() 会挂起?

标签 python multiprocessing ipython spyder

新年快乐!

我是 Python 新手 multiprocessing模块。为了更好地理解 apply_async有效,我在下面写下了简短的脚本。除非我将第二行注释掉( get_ipython().magic('reset -sf') ),否则脚本会挂起。
有人可以告诉我为什么会这样吗?我正在使用 Spyder IDE 在 Python 3.5 下工作。

我使用 IPython 魔术 %reset 的原因是因为我想在运行我的脚本之前清除所有变量,我阅读了 this webpage IPython 魔术 %reset 等同于 clear all来自 Matlab/Octave。

在此先感谢您的帮助!

from IPython import get_ipython
get_ipython().magic('reset -sf')
import random
import multiprocessing

def stakhanov(chunk_idx):
    data=random.randint(1,10) # create random integer between 1 and 10:
    frame_idx=chunk_idx
    return (frame_idx,data)

def stakhanov_finished(result):
    (frame_idx,data)=result
    DATA_READ[frame_idx]=data

def start_multiprocess_io():
    pool = multiprocessing.Pool(NUM_PROCESSES)  # create pool of all processes:
    chunk_idx = 0
    for i in range(NUM_PROCESSES):
        pool.apply_async(stakhanov,args=(chunk_idx,),callback=stakhanov_finished)
        chunk_idx += 1
    pool.close()
    pool.join() 

if __name__ == '__main__':
    global NUM_PROCESSES, DATA_READ
    NUM_PROCESSES = multiprocessing.cpu_count() # number of CORES
    DATA_READ = [None for _ in range(NUM_PROCESSES)] # declare list
    start_multiprocess_io()

最佳答案

好的,我不知道 get_ipython.magic 是什么call 确实如此,但是在没有人这样做的情况下,让我们看看多处理在 Windows 上是如何工作的,以及为什么这行:

get_ipython().magic('reset -sf')

可能是错的。可能,这应该隐藏在同一个 if __name__ == '__main__' 下测试你以后有。

(如果移动线路解决了问题,您可以在这里停下来,但如果您想有效地使用多处理代码,则值得阅读其余部分。)

当您创建 multiprocessing.ProcessPool例如,multiprocessing模块为新进程生成一个额外的 Python 实例。这和Linux类似,只是没有fork所以它不能复制当前进程。这个新生成的进程是一个全新的、新鲜的、空的 Python。

迄今为止空的 Python 使用特定的参数运行。这些在 Python 2.7 和 Python 3.6+ 之间有所不同;在这里,我将引用 2.7 中相当长的一段话:
def get_command_line():
    '''
    Returns prefix of command line used for spawning a child process
    '''
    if getattr(process.current_process(), '_inheriting', False):
        raise RuntimeError('''
        Attempt to start a new process before the current process
        has finished its bootstrapping phase.

        This probably means that you are on Windows and you have
        forgotten to use the proper idiom in the main module:

            if __name__ == '__main__':
                freeze_support()
                ...

        The "freeze_support()" line can be omitted if the program
        is not going to be frozen to produce a Windows executable.''')

    if getattr(sys, 'frozen', False):
        return [sys.executable, '--multiprocessing-fork']
    else:
        prog = 'from multiprocessing.forking import main; main()'
        opts = util._args_from_interpreter_flags()
        return [_python_exe] + opts + ['-c', prog, '--multiprocessing-fork']

3.6 代码将其拆分为 has this fragment :
if getattr(sys, 'frozen', False):
    return ([sys.executable, '--multiprocessing-fork'] +
            ['%s=%r' % item for item in kwds.items()])
else:
    prog = 'from multiprocessing.spawn import spawn_main; spawn_main(%s)'
    prog %= ', '.join('%s=%r' % item for item in kwds.items())
    opts = util._args_from_interpreter_flags()
    return [_python_exe] + opts + ['-c', prog, '--multiprocessing-fork']

无论哪种方式,此时发生的情况是新的 Python 应该从 multiprocessing 导入一个模块。并在该模块中运行一个函数。函数,main()spawn_main() ,从创建它的进程(您的进程)加载一些信息,以找出运行的程序。

这一切都取决于你到import multiprocessing并调用freeze_support ,如果您使用的是卡住的 Python。这是 if getattr(sys, 'frozen', False) 的第一个分支测试:这里正在解决的问题是 -c 'from multiprocessing ...'选项在卡住的 Python 中不起作用。 (如果你没有使用卡住的 Python,-c 行会处理大多数事情。)

无论如何,结果是你的新 Python 运行这个特殊的 mainspawn_main ,它连接回您的 Python 进程,即您自己启动的那个。从您的 Python 中,新的 Python 获取原始主模块的名称,并将其导入。

它使用常规的旧 import 导入它(嗯,有一个特殊的稍微修改过的导入,并且细节再次因 Python 版本而有所不同)。这意味着 __name__不是 __main__而是 mainprogram或任何您命名的main.py文件。这允许多处理代码访问您的整个程序。

接下来,多处理代码确定您想要从哪个模块运行什么功能。 (这一切都是通过 pickle 系统处理的,这就是为什么你只能运行可以腌制的函数,传递可以腌制的参数。)已经设置了原始 Python 和正在运行的新 Python 之间所需的所有通信进程,新的 Python 现在可以调用该函数,让它做它的事情,当它返回时,让新的 Python 进程终止。

所有这一切都取决于新的 Python 进程何时运行 import mainimport prog或任何使您的原始程序加载的东西,其可执行代码受使用 if __name__ 的测试保护。 .这可以确保该代码——你的程序的主要工作——不会在衍生的子 Python 中运行。相反,只有 multiprocessing.mainmultiprocessing.spawn_main实际运行。主程序中的所有内容都被导入和定义,因此一旦它们的名称通过酸洗代码出现,所有函数都可以被调用。但是它们都没有运行。

当且仅当它们不破坏运行 Process 所需的设置序列时,您可以违反此规则 1 并运行特定的代码。实例。根据这里看到的问题,这似乎很清楚,get_ipython.magic('reset -sf')打破设置顺序。

1您必须运行特定代码位的一种情况是,如果您必须增加 sys.path插入导入某些代码的位置。

关于python - 为什么使用 IPython 魔术 %reset 清除变量空间时 pool.join() 会挂起?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48053355/

相关文章:

python - 如何使用 joblib 并行化 scipy fftconvolve?

python-3.x - dask future 不会根据进度更新

python - 替换 IPython 菜单图标

python - 奇怪的 python 行为

python - 在 Python 中将包含文件名的列表转换为名称和后缀列表

python - 无法单击 selenium python 下拉菜单中的列表项

python - 快速 API - 如何在 GET 中显示来自 POST 的图像?

python - 多处理池的意外行为

ipython - 如何更改 IPython 控制台宽度

python - 无法从网页上抓取所有公司名称