为什么在使用IPython magic%reset来清除变量空间时pool.join()会挂起?

问题描述 投票:1回答:1

新年快乐!

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

我之所以使用IPython magic%reset是因为我想在运行脚本之前清除所有变量,并且我在this webpage上读到IPython magic%reset与Matlab / Octave中的clear all相当。

在此先感谢您的帮助!

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()
python multiprocessing ipython spyder
1个回答
2
投票

好吧,我不知道get_ipython.magic调用了什么,但是在没有人这样做的情况下,让我们来看看多处理在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())从创建它的进程中加载​​一些信息 - 您的进程 - 以找出运行的程序。

如果您使用的是冻结的Python,这一切都可能取决于您对import multiprocessing并调用freeze_support。这是if getattr(sys, 'frozen', False)测试的第一个分支:这里正在解决的问题是-c 'from multiprocessing ...'选项在冻结的Python中不起作用。 (如果你没有使用冷冻蟒蛇,那么-c系列可以处理大多数事情。)

无论如何,结果是你的新Python运行这个特殊的mainspawn_main,它连接回你的Python进程,你自己开始的那个。在Python中,新Python获取原始主模块的名称,然后导入它。

它使用常规的旧import导入它(好吧,有一个特殊的轻微入侵导入,并且Python版本的细节也有所不同)。这意味着__name__不是__main__,而是mainprogram或任何你命名为main.py文件。这允许多处理代码访问整个程序。

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

所有这一切都取决于以下事实:当新的Python进程运行import mainimport prog或其他任何原始程序加载时,其可执行代码受到使用if __name__的测试的保护。这确保了代码 - 您的程序的主要工作 - 不会在生成的子Python中运行。相反,只有multiprocessing.mainmultiprocessing.spawn_main实际运行。主程序中的所有内容都会被导入和定义,以便一旦通过酸洗代码显示其名称,就可以调用所有函数。但他们都没有跑。

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


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

© www.soinside.com 2019 - 2024. All rights reserved.