进程池和进程之间的通信
进程池
什么是进程池?
进程池就是预先创建好的进程的耳机和,可以向进程池中进程指派任务,不必反复创建和销毁进程。
使用进程池的好处:
批量创建多个进程,让程序执行完任务之后,再讲进程销毁,下次再有任务是,重新创建新的进程,这样频繁创建和销毁进程太浪费cpu。
更好的方式就是,预先创建好一批进程,为它们分配任务,任务完成之后,还可以再次给他们分配任务,而不是直接销毁。
创建进程池的步骤:
- 1、导入模块
from multiprocessing.pool import Pool
- 2、创建进程池对象,指定其中进程的个数:
my_pool = Pool(3) # 默认不写的话,进程池中进程个数默认是cpu的核心数 # os.cpu_count() 获取当前CPU的核心数
- 3、向进程中添加任务
my_pool.apply_async(要调用的目标,(传递给目标的参数元组)) # 异步添加,会将任务全部添加到进程池中 my_pool.apply(要调用的目标,(传递给目标的参数元组)) # 同步添加,一次只能添加一个任务,前一任务完成之后,才开始下一个。
- 4、关闭进程池,不再向其中添加任务,不会结束所有进程
my_pool.close()
- 5、等待进程池所有的进程结束
my_pool.join()
multiprocessing.Pool常用函数解析:
- apply_async(func[, args[, kwds]]) :使用非阻塞方式调用func(并行执行,堵塞方式必须等待上一个进程退出才能执行下一个进程),args为传递给func的参数列表,kwds为传递给func的关键字参数列表;
- apply(func[, args[, kwds]]):使用阻塞方式调用func
- close():关闭Pool,使其不再接受新的任务;
- terminate():不管任务是否完成,立即终止;
- join():主进程阻塞,等待子进程的退出, 必须在close或terminate之后使用;
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
任务执行完毕
进程和进程之间的通信-Queue
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对象时,若括号中没有指定最大可接收的消息数量,或数量为负值,那么就代表可接收的消息数量没有上限(直到内存的尽头)
- Queue.qsize():返回当前队列包含的消息数量
- Queue.empty():如果队列为空,返回True,反之False
- Queue.full():如果队列满了,返回True,反之False
- Queue.get([block[, timeout]]):获取队列中的一条消息,然后将其从队列中删除,block默认值为True
- 如果block使用默认值,且没有设置timeout(单位秒),消息队列如果为空,此时程序将被阻塞(停在读取状态),知道消息队列读到消息为止,如果设置了timeout,则会等待timeout秒,若还没有读取到任何消息,则会抛出“Queue.Empty”异常。
- 如果block返回值为False,消息队列为空,则会立刻抛出“Queue.Empty”异常。
- Queue.get_nowait():相当于Queue.get(False)
- Queue.put(item,[block[, timeout]]):将item消息写入队列,block默认值为True
- 如果block使用默认值,且没有设置timeout(单位秒),消息列队如果已经没有空间可写入,此时程序将被阻塞(停在写入状态),直到从消息列队腾出空间为止,如果设置了timeout,则会等待timeout秒,若还没空间,则抛出”Queue.Full”异常;
- 如果block值为False,消息列队如果没有空间可写入,则会立刻抛出”Queue.Full”异常;
- Queue.put_nowait(item):相当于Queue.put(item,False)
进程池中的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 进程,将要结束任务.......
主进程执行完毕