「这是我参与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的数量,并将其作为工作进程数。当有新的请求提交到进程池中时,如果进程池还没有满,就会创建一个新的进程来执行请求。如果进程池已满,请求就会被阻塞,直到池中有进程结束,才会创建新的进程来执行请求。
除了指定进程数目,在创建进程池时还能指定 initializer
和 initargs
参数,如果提供了这两个参数,则每个进程会在启动时调用 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, ])