进程池的使用 | 手把手教你入门Python之一百零九

简介: 当需要创建的子进程数量不多时,可以直接利用multiprocessing中的Process动态成生多个进程,但如果是上百甚至上千个目标,手动的去创建进程的工作量巨大,此时就可以用到multiprocessing模块提供的Pool方法。

上一篇:队列的使用 | 手把手教你入门Python之一百零八
下一篇:简单的HTTP服务器搭建| 手把手教你入门Python之一百一十

本文来自于千锋教育在阿里云开发者社区学习中心上线课程《Python入门2020最新大课》,主讲人姜伟。

进程池的使用

当需要创建的子进程数量不多时,可以直接利用multiprocessing中的Process动态成生多个进程,但如果是上百甚至上千个目标,手动的去创建进程的工作量巨大,此时就可以用到multiprocessing模块提供的Pool方法。

Pool

开启过多的进程并不能提高你的效率,反而会降低你的效率,假设有500个任务,同时开启500个进程,这500个进程除了不能一起执行之外(cpu没有那么多核),操作系统调度这500个进程,让他们平均在4个或8个cpu上执行,这会占用很大的空间。

如果要启动大量的子进程,可以用进程池的方式批量创建子进程:

def task(n):
    print('{}----->start'.format(n))
    time.sleep(1)
    print('{}------>end'.format(n))


if __name__ == '__main__':
    p = Pool(8)  # 创建进程池,并指定线程池的个数,默认是CPU的核数
    for i in range(1, 11):
        # p.apply(task, args=(i,)) # 同步执行任务,一个一个的执行任务,没有并发效果
        p.apply_async(task, args=(i,)) # 异步执行任务,可以达到并发效果
    p.close()
    p.join()
AI 代码解读

进程池获取任务的执行结果:

def task(n):
    print('{}----->start'.format(n))
    time.sleep(1)
    print('{}------>end'.format(n))
    return n ** 2


if __name__ == '__main__':
    p = Pool(4)
    for i in range(1, 11):
        res = p.apply_async(task, args=(i,))  # res 是任务的执行结果
        print(res.get())  # 直接获取结果的弊端是,多任务又变成同步的了
       p.close()
    # p.join()  不需要再join了,因为 res.get()本身就是一个阻塞方法
AI 代码解读

异步获取线程的执行结果:

import time
from multiprocessing.pool import Pool


def task(n):
    print('{}----->start'.format(n))
    time.sleep(1)
    print('{}------>end'.format(n))
    return n ** 2


if __name__ == '__main__':
    p = Pool(4)
    res_list = []
    for i in range(1, 11):
        res = p.apply_async(task, args=(i,))
        res_list.append(res)  # 使用列表来保存进程执行结果
    for re in res_list: 
        print(re.get())
    p.close()
AI 代码解读

初始化Pool时,可以指定一个最大进程数,当有新的请求提交到Pool中时,如果池还没有满,那么就会创建一个新的进程用来执行该请求;但如果池中的进程数已经达到指定的最大值,那么该请求就会等待,直到池中有进程结束,才会用之前的进程来执行新的任务,请看下面的实例:

from multiprocessing import Pool
import os, time, random


def worker(msg):
    t_start = time.time()
    print("%s开始执行,进程号为%d" % (msg, os.getpid()))
    # random.random()随机生成0~1之间的浮点数
    time.sleep(random.random() * 2)
    t_stop = time.time()
    print(msg, "执行完毕,耗时%0.2f" % (t_stop - t_start))


if __name__ == '__main__':
    po = Pool(3)  # 定义一个进程池,最大进程数3
    for i in range(0, 10):
        # Pool().apply_async(要调用的目标,(传递给目标的参数元祖,))
        # 每次循环将会用空闲出来的子进程去调用目标
        po.apply_async(worker, (i,))

    print("----start----")
    po.close()  # 关闭进程池,关闭后po不再接收新的请求
    po.join()  # 等待po中所有子进程执行完成,必须放在close语句之后
    print("-----end-----")
AI 代码解读

运行效果:

----start----
0开始执行,进程号为21466
1开始执行,进程号为21468
2开始执行,进程号为21467
0 执行完毕,耗时1.01
3开始执行,进程号为21466
2 执行完毕,耗时1.24
4开始执行,进程号为21467
3 执行完毕,耗时0.56
5开始执行,进程号为21466
1 执行完毕,耗时1.68
6开始执行,进程号为21468
4 执行完毕,耗时0.67
7开始执行,进程号为21467
5 执行完毕,耗时0.83
8开始执行,进程号为21466
6 执行完毕,耗时0.75
9开始执行,进程号为21468
7 执行完毕,耗时1.03
8 执行完毕,耗时1.05
9 执行完毕,耗时1.69
-----end-----
AI 代码解读

multiprocessing.Pool常用函数解析:

  • apply_async(func[, args[, kwds]]) :使用非阻塞方式调用func(并行执行,堵塞方式必须等待上一个进程退出才能执行下一个进程),args为传递给func的参数列表,kwds为传递给func的关键字参数列表;
  • close():关闭Pool,使其不再接受新的任务;
  • terminate():不管任务是否完成,立即终止;
  • join():主进程阻塞,等待子进程的退出, 必须在close或terminate之后使用;

进程池中的Queue

如果要使用Pool创建进程,就需要使用multiprocessing.Manager()中的Queue(),而不是multiprocessing.Queue(),否则会得到一条如下的错误信息:

RuntimeError: Queue objects should only be shared between processes through inheritance.
AI 代码解读

下面的实例演示了进程池中的进程如何通信:

# 修改import中的Queue为Manager
from multiprocessing import Manager, Pool
import os, time, random


def reader(q):
    print("reader启动(%s),父进程为(%s)" % (os.getpid(), os.getppid()))
    for i in range(q.qsize()):
        print("reader从Queue获取到消息:%s" % q.get(True))


def writer(q):
    print("writer启动(%s),父进程为(%s)" % (os.getpid(), os.getppid()))
    for i in "helloworld":
        q.put(i)


if __name__ == "__main__":
    print("(%s) start" % os.getpid())
    q = Manager().Queue()  # 使用Manager中的Queue
    po = Pool()
    po.apply_async(writer, (q,))

    time.sleep(1)  # 先让上面的任务向Queue存入数据,然后再让下面的任务开始从中取数据

    po.apply_async(reader, (q,))
    po.close()
    po.join()
    print("(%s) End" % os.getpid())
AI 代码解读

运行结果:

(4171) start
writer启动(4173),父进程为(4171)
reader启动(4174),父进程为(4171)
reader从Queue获取到消息:h
reader从Queue获取到消息:e
reader从Queue获取到消息:l
reader从Queue获取到消息:l
reader从Queue获取到消息:o
reader从Queue获取到消息:w
reader从Queue获取到消息:o
reader从Queue获取到消息:r
reader从Queue获取到消息:l
reader从Queue获取到消息:d
(4171) End
AI 代码解读

join方法的使用

# join 线程和进程都有join方法
import threading
import time

x = 10


def test(a, b):
    time.sleep(1)
    global x
    x = a + b


# test(1, 1)
# print(x)  # 2

t = threading.Thread(target=test, args=(1, 1))
t.start()
t.join()  # 让主线程等待

print(x)  # 10
AI 代码解读

配套视频

目录
打赏
0
0
0
0
285
分享
相关文章
Python入门修炼:开启你在大数据世界的第一个脚本
Python入门修炼:开启你在大数据世界的第一个脚本
54 6
Python创意爱心代码大全:从入门到高级的7种实现方式
本文分享了7种用Python实现爱心效果的方法,从简单的字符画到复杂的3D动画,涵盖多种技术和库。内容包括:基础字符爱心(一行代码实现)、Turtle动态绘图、Matplotlib数学函数绘图、3D旋转爱心、Pygame跳动动画、ASCII艺术终端显示以及Tkinter交互式GUI应用。每种方法各具特色,适合不同技术水平的读者学习和实践,是表达创意与心意的绝佳工具。
190 0
Python入门:8.Python中的函数
### 引言 在编写程序时,函数是一种强大的工具。它们可以将代码逻辑模块化,减少重复代码的编写,并提高程序的可读性和可维护性。无论是初学者还是资深开发者,深入理解函数的使用和设计都是编写高质量代码的基础。本文将从基础概念开始,逐步讲解 Python 中的函数及其高级特性。
Python入门:8.Python中的函数
Python入门:6.深入解析Python中的序列
在 Python 中,**序列**是一种有序的数据结构,广泛应用于数据存储、操作和处理。序列的一个显著特点是支持通过**索引**访问数据。常见的序列类型包括字符串(`str`)、列表(`list`)和元组(`tuple`)。这些序列各有特点,既可以存储简单的字符,也可以存储复杂的对象。 为了帮助初学者掌握 Python 中的序列操作,本文将围绕**字符串**、**列表**和**元组**这三种序列类型,详细介绍其定义、常用方法和具体示例。
Python入门:6.深入解析Python中的序列
Python入门:9.递归函数和高阶函数
在 Python 编程中,函数是核心组成部分之一。递归函数和高阶函数是 Python 中两个非常重要的特性。递归函数帮助我们以更直观的方式处理重复性问题,而高阶函数通过函数作为参数或返回值,为代码增添了极大的灵活性和优雅性。无论是实现复杂的算法还是处理数据流,这些工具都在开发者的工具箱中扮演着重要角色。本文将从概念入手,逐步带你掌握递归函数、匿名函数(lambda)以及高阶函数的核心要领和应用技巧。
Python入门:9.递归函数和高阶函数
Python入门:7.Pythond的内置容器
Python 提供了强大的内置容器(container)类型,用于存储和操作数据。容器是 Python 数据结构的核心部分,理解它们对于写出高效、可读的代码至关重要。在这篇博客中,我们将详细介绍 Python 的五种主要内置容器:字符串(str)、列表(list)、元组(tuple)、字典(dict)和集合(set)。
Python入门:7.Pythond的内置容器
Python 编程基础与实战:从入门到精通
本文介绍Python编程语言,涵盖基础语法、进阶特性及实战项目。从变量、数据类型、运算符、控制结构到函数、列表、字典等基础知识,再到列表推导式、生成器、装饰器和面向对象编程等高级特性,逐步深入。同时,通过简单计算器和Web爬虫两个实战项目,帮助读者掌握Python的应用技巧。最后,提供进一步学习资源,助你在Python编程领域不断进步。
Python 高级编程与实战:深入理解性能优化与调试技巧
本文深入探讨了Python的性能优化与调试技巧,涵盖profiling、caching、Cython等优化工具,以及pdb、logging、assert等调试方法。通过实战项目,如优化斐波那契数列计算和调试Web应用,帮助读者掌握这些技术,提升编程效率。附有进一步学习资源,助力读者深入学习。
[oeasy]python081_ai编程最佳实践_ai辅助编程_提出要求_解决问题
本文介绍了如何利用AI辅助编程解决实际问题,以猫屎咖啡的购买为例,逐步实现将购买斤数换算成人民币金额的功能。文章强调了与AI协作时的三个要点:1) 去除无关信息,聚焦目标;2) 将复杂任务拆解为小步骤,逐步完成;3) 巩固已有成果后再推进。最终代码实现了输入验证、单位转换和价格计算,并保留两位小数。总结指出,在AI时代,人类负责明确目标、拆分任务和确认结果,AI则负责生成代码、解释含义和提供优化建议,编程不会被取代,而是会更广泛地融入各领域。
80 28
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等