对Python中进程概念的理解(2)

进程池 Pool

当需要创建的子进程数量不多时,可以直接利用multiprocessing中的Process动态成生多个进程。
但如果是上百甚至上千个目标,手动的去创建进程的工作量巨大,此时就可以用到multiprocessing模块提供的Pool方法。

初始化Pool时,可以指定一个最大进程数,当有新的请求提交到Pool中时,如果池还没有满,那么就会创建一个新的进程用来执行该请求。
但如果池中的进程数已经达到指定的最大值,那么该请求就会等待,直到池中有进程结束,才会创建新的进程来执行。

from multiprocessing import Pool
import os
import time
import random


def worker(msg):
    t_start = time.time()
    print("%s开始执行,进程号为%d" % (msg, os.getpid()))
    time.sleep(random.random())
    t_stop = time.time()
    print(msg, "执行完毕,耗时%0.2f" % (t_stop - t_start))


po = Pool(3)

for i in range(1, 6):
    po.apply_async(worker, (i,))

print("----start----")
po.close()
po.join()
print("-----end-----")

代码运行结果:
这里写图片描述
multiprocessing.Pool常用函数:

apply_async(func[, args[, kwds]]) :使用非阻塞方式调用func(并行执行,堵塞方式必须等待上一个进程退出才能执行下一个进程),args为传递给func的参数列表,kwds为传递给func的关键字参数列表
apply(func[, args[, kwds]]):使用阻塞方式调用func
close():关闭Pool,使其不再接受新的任务
terminate():不管任务是否完成,立即终止
join():主进程阻塞,等待子进程的退出, 必须在close或terminate之后使用

同步和异步

  • 同步:调用一旦开始,调用者必须等到方法调用返回后,才能继续后续的行为。
  • 异步:一个任务不用等待其它任务执行完毕再执行,可以立刻开始,互相不影响。

进程间通信 Queue

可以使用multiprocessing模块的Queue实现多进程之间的数据传递,Queue本身是一个消息列队程序

Queue可以看作一个能够被所有进程调用修改的全局变量

from multiprocessing import Process, Queue
import time
import random


def write(q):
    for value in ['A', 'B', 'C']:
        print('Put %s to queue...' % value)
        q.put(value)
        time.sleep(random.random())


def read(q):
    while True:
        if not q.empty():
            value = q.get(True)
            print('Get %s from queue.' % value)
            time.sleep(random.random())
        else:
            break


if __name__ == '__main__':
    q = Queue()
    pw = Process(target=write, args=(q,))
    pr = Process(target=read, args=(q,))

    pw.start()
    pw.join()

    pr.start()
    pr.join()

    print('所有数据都写入并且读完')

代码运行结果:
运行结果

初始化Queue()对象时(例如:q=Queue()),若括号中没有指定最大可接收的消息数量,或数量为负值,那么就代表可接受的消息数量没有上限(直到内存的尽头)。

  • Queue.qsize():返回当前队列包含的消息数量;
  • Queue.empty():如果队列为空,返回True,反之False ;
  • Queue.full():如果队列满了,返回True,反之False;
  • Queue.get([block[, timeout]]):获取队列中的一条消息,然后将其从列队中移除,block默认值为True;

    1、如果block使用默认值,且没有设置timeout(单位秒),消息列队如果为空,此时程序将被阻塞(停在读取状态),直到从消息列队读到消息为止,如果设置了timeout,则会等待timeout秒,若还没读取到任何消息,则抛出”Queue.Empty”异常;
    2、如果block值为False,消息列队如果为空,则会立刻抛出”Queue.Empty”异常;

  • Queue.get_nowait():相当Queue.get(False);

  • Queue.put(item,[block[, timeout]]):将item消息写入队列,block默认值为True;

    1、如果block使用默认值,且没有设置timeout(单位秒),消息列队如果已经没有空间可写入,此时程序将被阻塞(停在写入状态),直到从消息列队腾出空间为止,如果设置了timeout,则会等待timeout秒,若还没空间,则抛出”Queue.Full”异常;
    2、如果block值为False,消息列队如果没有空间可写入,则会立刻抛出”Queue.Full”异常;

  • Queue.put_nowait(item):相当Queue.put(item, False);

进程池中的Queue

如果要使用Pool创建进程,需要使用multiprocessing.Manager()中的Queue(),而不是multiprocessing.Queue()。
此时需要修改import中的Queue为Manager,使用Manager中的Queue来初始化(如:q=Manager().Queue())。
否则会得到一条如下的错误信息:
RuntimeError: Queue objects should only be shared between processes through inheritance.

猜你喜欢

转载自blog.csdn.net/op0912036/article/details/81674306