热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Python子进程与子进程池的应用

Python的多线程受到GIL(GlobalInterpreterLock)的限制,GIL是一把加到了Python的解释器的锁,使得在任意时刻只允许一个

    Python 的多线程受到 GIL(Global Interpreter Lock) 的限制,GIL 是一把加到了 Python 的解释器的锁,使得在任意时刻只允许一个 Python 进程使用 Python 解释器,也就是任意时刻,Python 只有一个线程在运行。

    GIL 严重影响了计算密集型(CPU-bound) 的多线程程序,此时的多线程与单线程性能没什么差异,也发挥不了多核的威力。但对 I/O 密集型(I/O-bound) 影响不大,因为 CPU 多数时候是在等待。

    为了突破 GIL 的 CPU 密集型程序的限制,可以使用非 CPython 解释器,如 Jython, IronPython 或 PyPy, 更为现实的做法就是使用子进程来替代线程去承担较为繁重的计算任务,因为 GIL 是加在进程上的,所以新的进程有独立的 GIL.

新建子进程来处理任务

    需用到 multiprocessing.Process 类,这个类在 Python 2.6 就开始存在了。一般的编程语言创建一个进程是要去执行一个外部任命,然后获得它的输出,而 Python 可以像创建线程一样创建子进程,且欲执行的任务直接由 Python 在其中编写

from datetime import datetime
import time
from multiprocessing import Process
import osdef job(name):for _ in range(3):print('[Child {}][{}]'.format(os.getpid(), datetime.now()))time.sleep(1)print(f'sub process {os.getpid()} {name} done')if __name__ == '__main__':p = Process(target=job, args=('bob',)) # 留意如何向子进程传递参数p.start()print(f'main process {os.getpid()} done') # p.pid 可以得到子进程的 IDp.join() # 如果不 join 的话,主进程一退出,子进程也随即结束
from datetime import datetimeimport timefrom multiprocessing import Processimport osdef job ( name ) :for _ in range ( 3 ) :print ( '[Child {}][{}]' . format ( os . getpid ( ) , datetime . now ( ) ) )time . sleep ( 1 )print ( f 'sub process {os.getpid()} {name} done' )if __name__ == '__main__' :p = Process ( target = job , args = ( 'bob' , ) ) # 留意如何向子进程传递参数p . start ( )print ( f 'main process {os.getpid()} done' ) # p.pid 可以得到子进程的 IDp . join ( ) # 如果不 join 的话,主进程一退出,子进程也随即结束

执行后输出

main process 67474 done [Child][2021-09-02 19:32:14.121689] [Child][2021-09-02 19:32:15.126048] [Child][2021-09-02 19:32:16.129741] sub process 67476 bob done
可以看到主进程与子进程分别有自己不同的进程 ID。

    还有就是 if name == ‘main’ 变得是必要的语句了,不能把想要立即执行的代码丢在函数外了事,否则就是出现下面的错误信息

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.

    进程之间传递参数除了用 args=(‘bob’,) 的任务参数外,还可用 multiprocessing.Pipe 在进程之间双向通信。也可以通过内存来共享数据,用到 multiprocessing.Value 和 multiprocessing.Array 。这一部分的详细内容请参考官方文档,在实际编程中可能会经常用到。

    执行进程任务也可以加锁,用 multiprocessing.Lock , lock.acquire()…try: … finally: lock.release() 标准模式,因为进程之间需要保护对系统中唯一资源的竞争

在 Jupyter 中使用 Process 的问题

如果直接把上面的代码放到 JpyterLab 中去执行,将会看到这样的错误

Traceback (most recent call last):File "", line 1, in <module>File "/Users/yanbin/jupyterlab-venv/lib/python3.9/multiprocessing/spawn.py", line 116, in spawn_mainexitcode = _main(fd, parent_sentinel)File "/Users/yanbin/jupyterlab-venv/lib/python3.9/multiprocessing/spawn.py", line 126, in _mainself = reduction.pickle.load(from_parent)
AttributeError: Can't get attribute 'job' on __main__' (built-in)>
Traceback ( most recent call last ) :File "" , line 1 , in & lt ; module & gt ;File "/Users/yanbin/jupyterlab-venv/lib/python3.9/multiprocessing/spawn.py" , line 116 , in spawn_mainexitcode = _main ( fd , parent_sentinel )File "/Users/yanbin/jupyterlab-venv/lib/python3.9/multiprocessing/spawn.py" , line 126 , in _mainself = reduction . pickle . load ( from_parent )AttributeError : Can '
t get attribute ' job ' on <module ' __main_ _ ' ( built - in ) & gt ;

    这不是 Python 版本的问题,放别的 Python 3.8 下也是这样的错误,原因就是 Jupyter 无法与 multiprocessing 一同工作,pickle 模块在序列化数据向进程发送时出异常。解决办法是要用 multiprocess 替换掉 multiprocessing 模块

pip install multiprocess from multiprocess import Process

然后在 JupyterLab 中执行替换成 multiprocess 的版本,输出略有不同

[Child][2021-09-02 19:41:55.326549] main process 62917 done [Child][2021-09-02 19:41:56.335774] [Child][2021-09-02 19:41:57.342169] sub process 68144 bob done

    与在 Python 终端执行的一个区别是,子进程总有一行在 main process … 之前输出,这没什么要紧的。

使用进程池

有线程池,相应的也有进程池,参照一个官方文档中的简单例子

from multiprocessing import Pool
import osdef f(x):print(f'subprocess id: {os.getpid()}')return x*xif __name__ == '__main__':with Pool(5) as p:print(p.map(f, [1, 2, 3]))
from multiprocessing import Poolimport osdef f ( x ) :print ( f 'subprocess id: {os.getpid()}' )return x* xif __name__ == '__main__' :with Pool ( 5 ) as p :print ( p . map ( f , [ 1 , 2 , 3 ] ) )

输出为

subprocess id: 69348 subprocess id: 69350 subprocess id: 69347 [1, 4, 9]

这是用到了 Context 来管理进程池,如果逐步操作就是

pool = Pool(5)
[pool.apply_async(f, args=(i, )) for i in (1, 2, 3)]
pool.close()
pool.join()
pool = Pool ( 5 )[ pool . apply_async ( f , args = ( i , ) ) for i in ( 1 , 2 , 3 ) ]pool . close ( )pool . join ( )

进程池执行器

    这个翻译有点别扭,直接叫 ProcessPoolExecutor 习惯些。
ProcessPoolExecutor 在 concurrent.futures 模块中,它是 Python 3.2 加入进来的。至于用法呢,只要想像它是 ThreadPoolExecutor 的进程版本就差不多了。它提供的方法用

  1. submit(fn, /, *args, **kwargs): 向进程池提交任务
  2. map(func, *iterables, timeout=None, chunksize=1): 批量提交任务的快捷写法
  3. shutdown(wait=True, *, cancel_futures=False): 关闭进程池

首先仍然用一个使用了 with 关键字的写法

from concurrent.futures import ProcessPoolExecutor
import osdef f(x):return f'{os.getpid()}: {x*x}'if __name__ == '__main__':with ProcessPoolExecutor(max_workers=5) as executor:results = executor.map(f, [1, 3, 4])for i in results:print(i)
from concurrent . futures import ProcessPoolExecutorimport osdef f ( x ) :return f '{os.getpid()}: {x*x}'if __name__ == '__main__' :with ProcessPoolExecutor ( max_workers = 5 ) as executor :results = executor . map ( f , [ 1 , 3 , 4 ] )for i in results :print ( i )

输出如下:

    也可以用 submit() 函数来提交任务,得到的是一个 Future。关于 Future, 以及类似的 submit(), executor.map() 函数在Python 多线程编程 有所覆盖。

    另外,在构建 ProcessPoolExecutor 时如果不指 max_workers 参数将会取系统 CPU 的内核数(multiprocessing.cpu_count())。

    如果不对 ProcessPoolExecutor 使用 with 语句,则需要去用 submit() 提交的任务进行 wait,参照

在这里插入图片描述

    如果对软件测试、接口测试、自动化测试、持续集成、面试经验。感兴趣可以进到806549072,群内会有不定期的分享测试资料。还会有技术大牛,业内同行一起交流技术


推荐阅读
  • Python多线程编程技巧与实战应用详解 ... [详细]
  • 深入剖析Java中SimpleDateFormat在多线程环境下的潜在风险与解决方案
    深入剖析Java中SimpleDateFormat在多线程环境下的潜在风险与解决方案 ... [详细]
  • 本文介绍了如何利用 Delphi 中的 IdTCPServer 和 IdTCPClient 控件实现高效的文件传输。这些控件在默认情况下采用阻塞模式,并且服务器端已经集成了多线程处理,能够支持任意大小的文件传输,无需担心数据包大小的限制。与传统的 ClientSocket 相比,Indy 控件提供了更为简洁和可靠的解决方案,特别适用于开发高性能的网络文件传输应用程序。 ... [详细]
  • 在 Linux 环境下,多线程编程是实现高效并发处理的重要技术。本文通过具体的实战案例,详细分析了多线程编程的关键技术和常见问题。文章首先介绍了多线程的基本概念和创建方法,然后通过实例代码展示了如何使用 pthreads 库进行线程同步和通信。此外,还探讨了多线程程序中的性能优化技巧和调试方法,为开发者提供了宝贵的实践经验。 ... [详细]
  • 在Python多进程编程中,`multiprocessing`模块是不可或缺的工具。本文详细探讨了该模块在多进程管理中的核心原理,并通过实际代码示例进行了深入分析。文章不仅总结了常见的多进程编程技巧,还提供了解决常见问题的实用方法,帮助读者更好地理解和应用多进程编程技术。 ... [详细]
  • 探讨 `org.openide.windows.TopComponent.componentOpened()` 方法的应用及其代码实例分析 ... [详细]
  • MATLAB字典学习工具箱SPAMS:稀疏与字典学习的详细介绍、配置及应用实例
    SPAMS(Sparse Modeling Software)是一个强大的开源优化工具箱,专为解决多种稀疏估计问题而设计。该工具箱基于MATLAB,提供了丰富的算法和函数,适用于字典学习、信号处理和机器学习等领域。本文将详细介绍SPAMS的配置方法、核心功能及其在实际应用中的典型案例,帮助用户更好地理解和使用这一工具箱。 ... [详细]
  • Web开发框架概览:Java与JavaScript技术及框架综述
    Web开发涉及服务器端和客户端的协同工作。在服务器端,Java是一种优秀的编程语言,适用于构建各种功能模块,如通过Servlet实现特定服务。客户端则主要依赖HTML进行内容展示,同时借助JavaScript增强交互性和动态效果。此外,现代Web开发还广泛使用各种框架和库,如Spring Boot、React和Vue.js,以提高开发效率和应用性能。 ... [详细]
  • 本文探讨了如何利用Java代码获取当前本地操作系统中正在运行的进程列表及其详细信息。通过引入必要的包和类,开发者可以轻松地实现这一功能,为系统监控和管理提供有力支持。示例代码展示了具体实现方法,适用于需要了解系统进程状态的开发人员。 ... [详细]
  • 使用Maven JAR插件将单个或多个文件及其依赖项合并为一个可引用的JAR包
    本文介绍了如何利用Maven中的maven-assembly-plugin插件将单个或多个Java文件及其依赖项打包成一个可引用的JAR文件。首先,需要创建一个新的Maven项目,并将待打包的Java文件复制到该项目中。通过配置maven-assembly-plugin,可以实现将所有文件及其依赖项合并为一个独立的JAR包,方便在其他项目中引用和使用。此外,该方法还支持自定义装配描述符,以满足不同场景下的需求。 ... [详细]
  • C++ 开发实战:实用技巧与经验分享
    C++ 开发实战:实用技巧与经验分享 ... [详细]
  • 在Java编程中,`AbstractClassTest.java` 文件详细解析了抽象类的使用方法。该文件通过导入 `java.util.*` 包中的 `Date` 和 `GregorianCalendar` 类,展示了如何在主方法 `main` 中实例化和操作抽象类。此外,还介绍了抽象类的基本概念及其在实际开发中的应用场景,帮助开发者更好地理解和运用抽象类的特性。 ... [详细]
  • 本文探讨了如何优化时间格式查询,特别是针对 `yyyyMM` 和 `yyyyMMdd` 类型的时间格式,提出了有效的方法来检索上一个月的数据。通过使用 `SimpleDateFormat` 和 `Calendar` 类,我们实现了一个高效的函数,该函数接收一个字符串参数(如 `yyyy-MM`),并返回上一个月的对应日期。此方法不仅提高了查询效率,还增强了代码的可读性和可维护性。 ... [详细]
  • AIX编程挑战赛:AIX正方形问题的算法解析与Java代码实现
    在昨晚的阅读中,我注意到了CSDN博主西部阿呆-小草屋发表的一篇文章《AIX程序设计大赛——AIX正方形问题》。该文详细阐述了AIX正方形问题的背景,并提供了一种基于Java语言的解决方案。本文将深入解析这一算法的核心思想,并展示具体的Java代码实现,旨在为参赛者和编程爱好者提供有价值的参考。 ... [详细]
  • JavaScript XML操作实用工具类:XmlUtilsJS技巧与应用 ... [详细]
author-avatar
书友395154
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有