判断是否从没有主保护的函数中调用

2024-10-03 06:22:16 发布

您现在位置:Python中文网/ 问答频道 /正文

如果从没有主保护(if __name__ == '__main__':)的脚本导入模块,在模块中的某个函数中执行任何类型的并行操作都将导致Windows上的无限循环。每个新进程加载所有源,现在__name__不等于'__main__',然后继续并行执行。如果没有主保护,我们将在每个新进程中对同一个函数进行另一次调用,从而产生更多的进程,直到崩溃。这只是Windows上的一个问题,但是脚本也可以在osx和linux上执行。在

我可以通过写入磁盘上的一个特殊文件来检查这一点,并从中读取以查看我们是否已经开始,但这限制了我们一次只能运行一个python脚本。修改所有调用代码以添加主保护的简单解决方案是不可行的,因为它们分布在许多存储库中,而我无权访问这些存储库。因此,我希望在使用主保护的情况下进行并行化,但在不使用的情况下,我将回退到单线程执行。在

如何判断在导入循环中是否由于缺少主保护而被调用,以便可以回退到单线程执行?在

下面是一些演示代码:

带并行代码的库:

from multiprocessing import Pool


def _noop(x):
    return x


def foo():
    p = Pool(2)
    print(p.map(_noop, [1, 2, 3]))

好进口商(有警卫):

^{pr2}$

不良进口商(无防护):

from lib import foo

foo()

如果错误的导入程序因此运行时错误而失败,一次又一次:

    p = Pool(2)
  File "C:\Users\filip.haglund\AppData\Local\Programs\Python\Python35\lib\multiprocessing\context.py", line 118, in Pool
    context=self.get_context())
  File "C:\Users\filip.haglund\AppData\Local\Programs\Python\Python35\lib\multiprocessing\pool.py", line 168, in __init__
    self._repopulate_pool()
  File "C:\Users\filip.haglund\AppData\Local\Programs\Python\Python35\lib\multiprocessing\pool.py", line 233, in _repopulate_pool
    w.start()
  File "C:\Users\filip.haglund\AppData\Local\Programs\Python\Python35\lib\multiprocessing\process.py", line 105, in start
    self._popen = self._Popen(self)
  File "C:\Users\filip.haglund\AppData\Local\Programs\Python\Python35\lib\multiprocessing\context.py", line 313, in _Popen
    return Popen(process_obj)
  File "C:\Users\filip.haglund\AppData\Local\Programs\Python\Python35\lib\multiprocessing\popen_spawn_win32.py", line 34, in __init__
    prep_data = spawn.get_preparation_data(process_obj._name)
  File "C:\Users\filip.haglund\AppData\Local\Programs\Python\Python35\lib\multiprocessing\spawn.py", line 144, in get_preparation_data
    _check_not_importing_main()
  File "C:\Users\filip.haglund\AppData\Local\Programs\Python\Python35\lib\multiprocessing\spawn.py", line 137, in _check_not_importing_main
    is not going to be frozen to produce an executable.''')
RuntimeError: 
        An attempt has been made to start a new process before the
        current process has finished its bootstrapping phase.

        This probably means that you are not using fork to start your
        child processes 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 an executable.

Tags: toinpymainliblocallinemultiprocessing
1条回答
网友
1楼 · 发布于 2024-10-03 06:22:16

因为您使用的是multiprocessing,所以您还可以使用它来检测您是主进程还是子进程。但是,这些特性没有文档化,因此只是实现细节,在python版本之间可能会发生更改而不发出警告。在

每个进程有一个name_identity和{}。你可以检查他们中的任何一个,看看你是否在主进程中。在主过程中name将是'MainProcess'_identity将是{},而{}将是{})。在

我的解决方案允许您继续使用multiprocessing,但只修改子进程,使它们不能永远创建子进程。它使用修饰符将foo更改为子进程中的no-op,但在主进程中返回foo不变。这意味着当派生的子进程尝试执行foo时,不会发生任何事情(就像它是在__main__保护中执行的一样)。在

from multiprocessing import Pool
from multiprocessing.process import current_process

def run_in_main_only(func):
    if current_process().name == "MainProcess":
        return func
    else:
        def noop(*args, **kwargs):
            pass
        return noop

def _noop(_ignored):
    p = current_process()
    return p.name, p._identity, p._parent_pid

@run_in_main_only
def foo():
    with Pool(2) as p:
        for result in p.map(_noop, [1, 2, 3]):
            print(result) # prints something like ('SpawnPoolWorker-2', (2,), 10720)

if __name__ == "__main__":
    print(_noop(1)) # prints ('MainProcess', (), None)

相关问题 更多 >