热门标签 | 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,群内会有不定期的分享测试资料。还会有技术大牛,业内同行一起交流技术


推荐阅读
  • golang源码分析调度概述
    golang源码分析-调度过程概述本文主要概述一下golang的调度器的大概工作的流程,众所周知golang是基于用户态的协程的调度来完成多任务的执行。在Linux ... [详细]
  • 生成式对抗网络模型综述摘要生成式对抗网络模型(GAN)是基于深度学习的一种强大的生成模型,可以应用于计算机视觉、自然语言处理、半监督学习等重要领域。生成式对抗网络 ... [详细]
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • 本文介绍了使用Python根据字典中的值进行排序的方法,并给出了实验结果。通过将字典转化为记录项,可以按照字典中的值进行排序操作。实验结果显示,按照值进行排序后的记录项为[('b', 2), ('a', 3)]。 ... [详细]
  • 先看官方文档TheJavaTutorialshavebeenwrittenforJDK8.Examplesandpracticesdescribedinthispagedontta ... [详细]
  • 在开发中,有时候一个业务上要求的原子操作不仅仅包括数据库,还可能涉及外部接口或者消息队列。此时,传统的数据库事务无法满足需求。本文介绍了Java中如何利用java.lang.Runtime.addShutdownHook方法来保证业务线程的完整性。通过添加钩子,在程序退出时触发钩子,可以执行一些操作,如循环检查某个线程的状态,直到业务线程正常退出,再结束钩子程序。例子程序展示了如何利用钩子来保证业务线程的完整性。 ... [详细]
  • 开发笔记:python协程的理解
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了python协程的理解相关的知识,希望对你有一定的参考价值。一、介绍什么是并发?并发的本质就是 ... [详细]
  • go channel 缓冲区最大限制_Golang学习笔记之并发.协程(Goroutine)、信道(Channel)
    原文作者:学生黄哲来源:简书Go是并发语言,而不是并行语言。一、并发和并行的区别•并发(concurrency)是指一次处理大量事情的能力 ... [详细]
  • 我们需要用到一个python强大的库:python-dox直接pipinstallpython-docx在项目中加个赤壁赋.docx的空文件附上完整的python代码fromdoc ... [详细]
  • 丛api的python的简单介绍
    本文目录一览:1、如何使用python利用api获取天气预报 ... [详细]
  • sklearn数据集库中的常用数据集类型介绍
    本文介绍了sklearn数据集库中常用的数据集类型,包括玩具数据集和样本生成器。其中详细介绍了波士顿房价数据集,包含了波士顿506处房屋的13种不同特征以及房屋价格,适用于回归任务。 ... [详细]
  • [大整数乘法] java代码实现
    本文介绍了使用java代码实现大整数乘法的过程,同时也涉及到大整数加法和大整数减法的计算方法。通过分治算法来提高计算效率,并对算法的时间复杂度进行了研究。详细代码实现请参考文章链接。 ... [详细]
  • Spring常用注解(绝对经典),全靠这份Java知识点PDF大全
    本文介绍了Spring常用注解和注入bean的注解,包括@Bean、@Autowired、@Inject等,同时提供了一个Java知识点PDF大全的资源链接。其中详细介绍了ColorFactoryBean的使用,以及@Autowired和@Inject的区别和用法。此外,还提到了@Required属性的配置和使用。 ... [详细]
  • 本文介绍了Python语言程序设计中文件和数据格式化的操作,包括使用np.savetext保存文本文件,对文本文件和二进制文件进行统一的操作步骤,以及使用Numpy模块进行数据可视化编程的指南。同时还提供了一些关于Python的测试题。 ... [详细]
  • 如何使用Python从工程图图像中提取底部的方法?
    本文介绍了使用Python从工程图图像中提取底部的方法。首先将输入图片转换为灰度图像,并进行高斯模糊和阈值处理。然后通过填充潜在的轮廓以及使用轮廓逼近和矩形核进行过滤,去除非矩形轮廓。最后通过查找轮廓并使用轮廓近似、宽高比和轮廓区域进行过滤,隔离所需的底部轮廓,并使用Numpy切片提取底部模板部分。 ... [详细]
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社区 版权所有