什么是进程池?
进程池就是预先创建好的进程的耳机和,可以向进程池中进程指派任务,不必反复创建和销毁进程。
使用进程池的好处:
批量创建多个进程,让程序执行完任务之后,再讲进程销毁,下次再有任务是,重新创建新的进程,这样频繁创建和销毁进程太浪费cpu。
更好的方式就是,预先创建好一批进程,为它们分配任务,任务完成之后,还可以再次给他们分配任务,而不是直接销毁。
创建进程池的步骤:
from multiprocessing.pool import Pool
my_pool = Pool(3) # 默认不写的话,进程池中进程个数默认是cpu的核心数
# os.cpu_count() 获取当前CPU的核心数
my_pool.apply_async(要调用的目标,(传递给目标的参数元组)) # 异步添加,会将任务全部添加到进程池中
my_pool.apply(要调用的目标,(传递给目标的参数元组)) # 同步添加,一次只能添加一个任务,前一任务完成之后,才开始下一个。
my_pool.close()
my_pool.join()
multiprocessing.Pool常用函数解析:
Demo:
import os
import random
from multiprocessing.pool import Pool
import time
def task(id):
# 开始时间
start_time = time.time()
print(">>>> %d 任务开始执行, pid = %s" % (id, os.getpid()))
time.sleep(random.random() * 2)
# 结束时间
end_time = time.time()
print(">>>> %d 任务执行完毕, pid = %s, 耗时:%.2f" % (id, os.getpid(), (end_time - start_time)))
def main():
print("父进程pid = %d" % os.getpid())
# 1、创建进程池 指定其中进程的个数
my_pool = Pool(4)
# 2、向进程中添加任务
for _id in range(10):
my_pool.apply_async(task, (_id,))
print("任务添加完成")
# 3、关闭进程池,不再向其中添加任务
my_pool.close()
# 4、等待进程池中所有的进程结束
my_pool.join()
print("任务执行完毕")
if __name__ == '__main__':
main()
运行结果:
父进程pid = 14908
任务添加完成
>>>> 0 任务开始执行, pid = 15392
>>>> 1 任务开始执行, pid = 6672
>>>> 2 任务开始执行, pid = 4452
>>>> 3 任务开始执行, pid = 6236
>>>> 2 任务执行完毕, pid = 4452, 耗时:0.11
>>>> 4 任务开始执行, pid = 4452
>>>> 3 任务执行完毕, pid = 6236, 耗时:0.70
>>>> 5 任务开始执行, pid = 6236
>>>> 5 任务执行完毕, pid = 6236, 耗时:0.38
>>>> 6 任务开始执行, pid = 6236
>>>> 4 任务执行完毕, pid = 4452, 耗时:1.55
>>>> 7 任务开始执行, pid = 4452
>>>> 0 任务执行完毕, pid = 15392, 耗时:1.75
>>>> 8 任务开始执行, pid = 15392
>>>> 7 任务执行完毕, pid = 4452, 耗时:0.08
>>>> 9 任务开始执行, pid = 4452
>>>> 1 任务执行完毕, pid = 6672, 耗时:1.79
>>>> 8 任务执行完毕, pid = 15392, 耗时:0.15
>>>> 9 任务执行完毕, pid = 4452, 耗时:0.84
>>>> 6 任务执行完毕, pid = 6236, 耗时:1.76
任务执行完毕
Demo:
import os
import random
from multiprocessing import Queue, Process
import time
def write_task(q):
for s in ("Hello", "World", "Python", "end"):
q.put(s)
print("%s 进程,正在添加数据----%s" % (os.getpid(), s))
time.sleep(random.random() * 2)
print("%s 进程, 将要完成任务" % os.getpid())
def read_task(q):
while True:
msg = q.get()
if msg == "end":
print("%s 进程将要结束。。。" % os.getpid())
break
print("%s 进程,正在读取数据---%s" % (os.getpid(), msg))
time.sleep(random.random() * 2)
def main():
# 创建消息队列
q = Queue()
# 创建进程,
_write = Process(target=write_task, args=(q,))
_read = Process(target=read_task, args=(q,))
# 启动进程
_write.start()
_read.start()
_read.join()
_read.join()
print("父进程完成任务了..............")
if __name__ == '__main__':
main()
运行结果:
15580 进程,正在添加数据----Hello
9596 进程,正在读取数据---Hello
15580 进程,正在添加数据----World
9596 进程,正在读取数据---World
15580 进程,正在添加数据----Python
15580 进程,正在添加数据----end
15580 进程, 将要完成任务
9596 进程,正在读取数据---Python
9596 进程将要结束。。。
父进程完成任务了..............
说明:
初始化Queue对象时,若括号中没有指定最大可接收的消息数量,或数量为负值,那么就代表可接收的消息数量没有上限(直到内存的尽头)
如果要使用Pool创建进程,就需要使用multiprocessing.Manager()中的Queue(),而不是multiprocessing.Queue(),否则会得到一条如下的错误信息:
RuntimeError: Queue objects should only be shared between processes through inheritance.
Demo:
import os
import random
from multiprocessing.pool import Pool
from multiprocessing import Manager
import time
def write_task(q):
for s in ("Hello", "World", "Python", "end"):
q.put(s)
print("%s 进程,正在添加数据---%s" % (os.getpid(), s))
time.sleep(random.random() * 2)
print("%s 进程,将要结束任务......." % os.getpid())
def read_task(q):
while True:
msg = q.get()
if msg == "end":
print("%s 进程,将要读取完毕...." % os.getpid())
break
print("%s 进程读取到消息%s" % (os.getpid(), msg))
time.sleep(random.random() * 2)
def main():
# 创建进程间通讯的工具
q = Manager().Queue() # 进程池中使用消息队列,必须使用Manager().Queue()
# 创建线程池对象
p = Pool(2)
# 使用同步添加任务的话,程序只能一个任务一个的添加,只能先put再get
p.apply_async(write_task, (q,))
p.apply_async(read_task, (q,))
p.close()
p.join()
print("主进程执行完毕")
if __name__ == '__main__':
main()
执行效果 :
11532 进程,正在添加数据---Hello
14016 进程读取到消息Hello
11532 进程,正在添加数据---World
14016 进程读取到消息World
11532 进程,正在添加数据---Python
14016 进程读取到消息Python
11532 进程,正在添加数据---end
14016 进程,将要读取完毕....
11532 进程,将要结束任务.......
主进程执行完毕