Python中if __name__=="__main__" 语句在调用多进程Process过程中的作用分析
2018年2月27日 于创B515
引言
最近准备学习一下如何使用Python中的多进程。在翻看相关书籍、网上资料时发现所有代码都含有if __name__=="__main__",在实验的过程中发现如果在运行代码过程中,没有这句话Python解释器就会报错。虽然Python对于multiprocessing的文档第17.2.1.1节中【1】提到必须如此使用,但是我觉得并没有根本上解释清楚。因此我决定从源码来解释我的疑惑。
# 代码0.1错误代码
import multiprocessing as mp
import os def do():
print("pid is : %s ..." % os.getpid()) print("parent id is : %s ..." % os.getpid())
p = mp.Process(target=do, args=())
p.start()
# 代码0.2正确代码
import multiprocessing as mp
import os def do():
print("pid is : %s ..." % os.getpid()) if __name__ == '__main__':
print("parent id is : %s ..." % os.getpid())
p = mp.Process(target=do, args=())
p.start()
问题描述
问题
在运行代码-0.1时,会出现RuntimeError,错误提示如下。但是运行代码0.2时就不会,一切顺利。
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.
问题产生的环境
| 运行环境: | Win10 |
| IDE | Sublime Text3 |
简单解释
由于Python运行过程中,新创建进程后,进程会导入正在运行的文件,即在运行代码0.1的时候,代码在运行到mp.Process时,新的进程会重新读入改代码,对于没有if __name__=="__main__"保护的代码,新进程都认为是要再次运行的代码,这是子进程又一次运行mp.Process,但是在multiprocessing.Process的源码中是对子进程再次产生子进程是做了限制的,是不允许的,于是出现如上的错误提示。
详细解释
先谈一谈if__name__=="__main__"
在Python有关__main__的文档中【2】说明“__main__”是代码执行时的最高的命名空间(the name of the scope in which top-level code executes),当代码被当做脚本读入的时候,命名空间会被命名为“__main__”,对于在脚本运行过程中读入的代码命名空间都不会被命名为“__main__”。这也就是说创建的子进程是不会读取__name__=="__main__"保护下的代码。
再谈一谈multiprocessing(win32下的源码分析)
multiprocessing根据平台不同会执行不同的代码:在类UNIX系统下由于操作系统本身支持fork()语句,win32系统由于本身不支持fork(),因此在两种系统下multiprocessing会运行不同的代码,如图1 UNIX平台、图2 win32平台(包含在context.py文件中,Process的定义也是在context.py文件中)。

图1 对于类UNIX系统平台

图2 对于win32系统平台
Process是一个高度依赖继承的类———其父类是BaseProcess,如图3 Process类的定义。在使用过程中先初始化一个Process实例,然后通过Process.start()来启动子进程。我们继续看关于Process.start()的定义,如图4 BaseProcess类中的start()定义。在其中第105行,调用了self._Popen(self),该函数重定义定义于Process类。该函数按调用顺序最后会跳转到 popen_spwan_win32.py 文件中的Popen类,如图5 Popen类的定义。Popen类在初始话过程中首先调用windows相关接口,生成一个管道(38行),取得对管道读写的句柄,然后生成一个命令行的字符串列表——cmd,如下:
['C:\\Program Files\\Python35\\python.exe', '-B', '-c',
'from multiprocessing.spawn import spawn_main;spawn_main(pipe_handle=928, parent_pid=9292)',
'--multiprocessing-fork']

图3 Process类的定义

图4 BaseProcess类中的start()定义


图5 Popen类的定义
这个字符串列表之后通过命令行送入系统,得到相应的子进程和子线程的句柄及子进程和子线程的ID号。在第65、66行是通过pickle方法将必要的数据从父进程传输给子进程。新进程是调用spawn.py文件中的spawn_main函数,如图6 spawnmain的定义。其中第100行的steal_handle()的作用是子进程获取父进程生成的句柄,用于后续通信——使用pickle方法从父进程中读取必要数据。而问题的出现就是在这之后出现!该代码在第106行调用_main(),其次在第115行调用prepare(),再次运行到223行时,运行_fixup_main_from_name(),而此时该函数会运行父进程的脚本。因此对于没有if__name__=="__main__"保护的代码都是要运行的,而此时在第二次运行Process创建新进程的时候在第123行 if getattr(process.current_process(), '_inheriting', False): 时,由于子进程是具有_inheriting属性,因此会激发出上述错误代码。

图6 spawn_main的定义
对于代码中生成新子进程时所用到的两个技术我觉很有趣也很苦恼,因此决定继续看下去。下面简单描述一下两个技术的概况。
—— pickle模块
首先是pickle模块。pickle模块是一个Python中特有的数据格式,与JSON等不同,是不能被其他语言识别的格式。在Python中的官方文档【3】中比较了pickle模块与JSON的不同之处,以及介绍了pickle的使用条件。简单摘录如下:
|
|
—— 管道(Pipe)
这里理解管道是从类UNIX系统理解,因为其理解起来更方便。管道在类UNIX系统中也是一种文件,在生成新的子进程的时候将两个进程都关联至同一个管道上,这样就是双工通信(父子进程相互可读可写)。如果要实现单工通信,就关闭相应的通道(一方写一方读)【4】。
参考文献
- Python3.5 关于multiprocessing的文档 https://docs.python.org/3.5/library/multiprocessing.html
- Python3.5 关于__main__的文档 https://docs.python.org/3.5/library/__main__.html
- Python3.5 关于pickle的文档 https://docs.python.org/3.5/library/pickle.html?highlight=pickle#module-pickle
- 网上一片关于pipe的介绍 http://www.cnblogs.com/qiaoyanlin/p/7576085.html
Python中if __name__=="__main__" 语句在调用多进程Process过程中的作用分析的更多相关文章
- Python中的if __name__='__main__'语句的作用
笔者在自学Python的过程中,对于if __name__='__main__'的用法感到很困惑,在think Python一书中原作者的源代码是这么解释if __name__='__main__'语 ...
- python中if __name__ == '__main__'
python 中__name__ = '__main__' 的作用,到底干嘛的? 有句话经典的概括了这段代码的意义: “Make a script both importable and execut ...
- Python 中if __name__ == '__main__': 的作用和原理
转自https://blog.csdn.net/weixin_42660771/article/details/84035153 1.代码的功能 一个python的文件有两种使用的方法,第一是直接作为 ...
- Python常见经典 python中if __name__ == '__main__': 的解析
当你打开一个.py文件时,经常会在代码的最下面看到if __name__ == '__main__':,现在就来介 绍一下它的作用. 模块是对象,并且所有的模块都有一个内置属性 __name__.一个 ...
- Python中if __name__ == '__main__':的作用和原理
if __name__ == '__main__':的作用 一个python文件通常有两种使用方法,第一是作为脚本直接执行,第二是 import 到其他的 python 脚本中被调用(模块重用)执行. ...
- python 中if __name__ = '__main__' 的作用
python 中if __name__ = '__main__' 的作用 前言 首先我们要知道在python里面万物皆对象,模块也是对象,并且所有的模块都有一个内置属性 __name__. 一个模块的 ...
- Python中“if __name__=='__main__':”理解与总结
1 引言 在Python当中,如果代码写得规范一些,通常会写上一句“if __name__==’__main__:”作为程序的入口,但似乎没有这么一句代码,程序也能正常运行.这句代码多余吗?原理又在哪 ...
- python中if __name__ == '__main__': 解析
当你打开一个.py文件时,经常会在代码的最下面看到if __name__ == '__main__':,现在就来介 绍一下它的作用. 模块是对象,并且所有的模块都有一个内置属性 __name__.一 ...
- python 中的__name__ == "__main__"(转)
有句话经典的概括了这段代码的意义: “Make a script both importable and executable” 意思就是说让你写的脚本模块既可以导入到别的模块中用,另外该模块自己也可 ...
随机推荐
- rpm命令的使用
如果服务器配置了本地yum源,大部分的常用软件包都是有的.如果差了头文件或者什么so文件,可以按如下方式找:#以memory.h为例 [root@sz-cdn-centos7-1 tmp]# wher ...
- <global-results>标签来定义全局的<result>
<global-results> <result name="error">/Error.jsp</result> <!-- Ac ...
- CentOS 7修改网卡名称
CentOS 7 修改网卡名为eth0 标签: linux 笔者Q:972581034 交流群:605799367.有任何疑问可与笔者或加群交流 CentOS7的网卡名称太长,这不符合我们的使用习惯, ...
- 怎样共享windows和linux之间的文件
注:本文参考自:https://www.howtogeek.com/176471/how-to-share-files-between-windows-and-linux/,相当于是原文的翻译. 一. ...
- 《.NET 设计规范》第 4 章:类型设计规范
第 4 章:类型设计规范 4.1 类型和命名空间 要用命名空间把类型组织成一个由相关的功能区所构成的层次结构中. 避免非常深的命名空间层次.因为用户需要经常回找,所以这样的层次浏览起来很困难. 避免有 ...
- spring-boot 使用 main函数 无法启动的问题完美 解决方案。
首先 是启动之后 ,直接回exit code 0,网址 里面输入localhost:8080显示站点未启动.网上查 了多种 方式 ,日志 也 打了,都没发现问题,最后到这篇文章里 找到了答案.但是这 ...
- 利用多进程获取猫眼电影top100
猫眼电影top100 是数据是在加载网页时直接就已经加载了的,所以可以通过requests.get()方法去获取这个url的数据,能过对得到的数据进行分析从而获得top100的数据, 把获取的数据存入 ...
- python3中,os.path模块下常用的用法总结
abspath basename dirname exists getatime getctime getmtime getsize isabs isdir isfile islink ismount ...
- 51NOD 1821 最优集合 [并查集]
传送门 题意: 一个集合S的优美值定义为:最大的x,满足对于任意i∈[1,x],都存在一个S的子集S',使得S'中元素之和为i. 给定n个集合,对于每一次询问,指定一个集合S1和一个集合S2,以及一个 ...
- Orleans之EventSourcing
Orleans之EventSourcing 这是Orleans系列文章中的一篇.首篇文章在此 引入: 如果没有意外,我再这篇文章中用ES代替EventSourcing,如果碰到"事件回溯&q ...