【Python】多进程-进程池

1,907 阅读2分钟

「这是我参与2022首次更文挑战的第15天,活动详情查看:2022首次更文挑战」。

在之前的一系列文章中,已经介绍了如何创建多个进程,多个进程间如何使用互斥锁、信号量进行同步,如何使用队列和管道通信,今天将是进程系列的最后一个知识点:进程池

进程池的作用

回顾一下我们之前的代码,在生产者-消费者问题中,为了创建多个消费者进程,我们是这样写的:

p2 = multiprocessing.Process(target= customer, args=(conn2, ))
p3 = multiprocessing.Process(target= customer, args=(conn2, )) 
p4 = multiprocessing.Process(target= customer, args=(conn2, ))

可以看到,创建多个进程非常麻烦,创建每个进程都得用掉一个语句,而且是一模一样的语句。同样的,进程的开启也需要多行类似的语句。那么如何解决这种冗余的情况呢,就要使用到进程池了。看看相同的操作用进程池的实现:

with multiprocessing.Pool(3) as p:
    p.map(customer, [conn2, conn2, conn2,])

进程池的创建和使用

首先,我们使用 multiprocessing.Pool(3) 创建了一个进程池,其中 3 指的是工作进程的数目,如果不提供这个参数,则会使用 os.cpu_count() 获取CPU的数量,并将其作为工作进程数。当有新的请求提交到进程池中时,如果进程池还没有满,就会创建一个新的进程来执行请求。如果进程池已满,请求就会被阻塞,直到池中有进程结束,才会创建新的进程来执行请求。

除了指定进程数目,在创建进程池时还能指定 initializerinitargs 参数,如果提供了这两个参数,则每个进程会在启动时调用 initializer(initargs)

创建好进程池后,我们就可以向其中提交请求了。我们使用 map(func, iterable) 向进程池提交要执行的请求。和内置的 map(func, iterable) 函数功能相似,iterable 中的每个元素被作为参数,用于调用 func 函数。请求池为请求创建进程并运行,并阻塞直到线程运行结果返回。如果希望不阻塞,则可以使用 map_async()

除此之外,与多线程相似,一些方法用于进程池的控制。如 close() 用于关闭进程池,使其不再接受新的任务;terminal() 用于结束工作进程池,不再处理未处理的任务;join() 会阻塞主进程,直到子进程全部退出。

代码

最后,附上使用进程池实现的生产者-消费者问题的代码。

import multiprocessing
import time 
import random

class cake:
    def __init__(self, time):
        self.productionTime = time 
    
    def eat(self):
        print("Eat a cake product at ", self.productionTime)
    

def cook(conn):
    for _ in range(8):
        conn.send(cake(time.time()))
        time.sleep(random.random())
    conn.close()

def customer(conn):
    for _ in range(2):
        cake = conn.recv()
        cake.eat()
        time.sleep(random.random() * 5)

if __name__ == "__main__":

    conn1, conn2 = multiprocessing.Pipe()

    p1 = multiprocessing.Process(target= cook, args=(conn1, ))
    p1.start()

    with multiprocessing.Pool(3) as p:
        p.map(customer, [conn2, conn2, conn2, ])