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

时间:2018-01-01 21:26:14

标签: python multiprocessing ipython spyder

新年快乐!

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

我使用IPython魔术%重置的原因是因为我想在运行脚本之前清除所有变量,并且我在this webpage上读到IPython魔术%重置等同于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()

1 个答案:

答案 0 :(得分:2)

好的,我不知道get_ipython.magic调用的作用,但是如果没有人这样做,让我们看看多处理在Windows上是如何工作的,以及为什么这一行: / p>

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 并运行特定的代码,当且仅当他们不打破运行{所需的设置顺序时{1}}实例。基于这里看到的问题,似乎很清楚Process打破了设置顺序。

1 如果必须增加get_ipython.magic('reset -sf')以插入导入某些代码的位置,必须运行特定代码位的一种情况。< / p>