进程池和进程之间的通信

进程池和进程之间的通信

进程池

  • 什么是进程池?

    进程池就是预先创建好的进程的耳机和,可以向进程池中进程指派任务,不必反复创建和销毁进程。

  • 使用进程池的好处:

    批量创建多个进程,让程序执行完任务之后,再讲进程销毁,下次再有任务是,重新创建新的进程,这样频繁创建和销毁进程太浪费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 进程,将要结束任务.......
主进程执行完毕

你可能感兴趣的:(Python-高级)