进程之间有时候需要通信,可以使用 multiprocessing 模块中的 Queue 类来实现进程间的数据传递,Queue 是一个消息队列,循序 先进先出 的规则;

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

 

Queue 常用的方法:

  • Queue.qsize():返回当前消息队列包含的消息数量;
  • Queue.empty():如果队列为空,返回 True,否则返回 False;
  • Queue.full():如果队列满了,返回 True,否则返回 False;
  • Queue.get([block[, timeout]]):获取队列中的一条消息,然后将其从队列中移除,block 默认值为 True;
    (1)、如果 block 使用默认值(True),并且没有设置 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 使用默认值(True),并且没有指定 timeout,此时如果消息队列已满,程序将被阻塞(停在写入状态),直到从消息队列腾出空间为止;如果设置了 timeout,则会等待 timeout 秒,若还没有空间,则抛出 "Queue.full" 异常;
    (2)、如果 block 值为 False,并且消息队列已满,则会立刻抛出 "Queue.full" 异常;
  • Queue.put_nowait(item):相当于 Queue.put(item, False);

 

下面例子用于演示 Queue 的工作原理:

# 导入 multiprocessing 模块中的 Queue
from multiprocessing import Queue

# 初始化一个 Queue 对象,最多可以储存 3 个消息
queue = Queue(3)

print(queue.empty())    # True:表示消息队列是空的

# 向队列中添加消息
queue.put("消息1")
queue.put("消息2")
print(queue.full())     # False:表示消息队列未满

queue.put("消息3")
print(queue.full())     # True:表示消息队列已满

# 获取消息队列中的消息数量
print("队列中的消息数量:", queue.qsize())

try:
    # 如果消息队列已满,再向消息队列中添加消息时,会等待;
    # 如果 block 为 True,则等待指定的秒数,直到等待时间结束,
    # 如果消息队列还是满的,则会抛出 "Queue.full" 异常;
    queue.put("消息4", True, 2)
except:
    print("向消息队列中添加消息异常")

try:
    # 消息队列已满,再向消息队列中添加消息时会立刻抛出异常;
    # 相当于 queue.put("消息4", False)
    queue.put_nowait("消息4")
    # queue.put("消息4", False)
except:
    print("向消息队列中添加消息失败")

# 推荐的方式:先判断消息队列是否已满,再写入
if not queue.full():
    queue.put_nowait("消息4")
else:
    print("消息队列已满")

# 先判断消息队列是否为空,再读取
if not queue.empty():
    # 获取队列中的一条消息,然后将其从队列中移除
    print(queue.get())
    print("剩余消息数量:", queue.qsize())
    print(queue.get())
    print("剩余消息数量:", queue.qsize())

 

下面例子用于演示两个进程间的通信:一个进程向消息队列中写入数据,另一个进程从消息队列中读取数据:

# 导入 multiprocessing 模块中的 Process 和 Queue
from multiprocessing import Process, Queue
import time

# 向消息队列中写入数据
def write(queue):
    # 循环从列表中取出数据写入到队列中
    for value in ["how", "are", "you"]:
        print("put %s to queue" %value)
        queue.put(value)
        time.sleep(1)

# 从消息队列中读取数据
def read(queue):
    while True:
        # 如果队列不为空,则从队列中读取数据
        if not queue.empty():
            print("get %s from queue" %(queue.get()))
            time.sleep(1)
        # 如果队列为空,则退出循环
        else:
            break

if __name__ == "__main__":
    # 主进程中创建 queue 对象,传给各个子进程
    queue = Queue()

    # 创建子进程,执行写操作
    pWrite = Process(target=write, args=(queue, ))
    # 创建子进程,执行读操作
    pRead = Process(target=read, args=(queue, ))

    pWrite.start()  # 启动子进程,开始写操作
    pWrite.join()   # 等待写操作执行结束,再继续向下执行

    pRead.start()   # 启动子进程,开始读操作
    pRead.join()    # 等待读操作执行结束,再继续向下执行

 

进程池中的 Queue:

上面使用的 multiprocessing.Queue 只能用于通过 Process 创建的子进程之间进行通信;

如果是通过进程池 Pool 方式创建的子进程间,就需要使用 multiprocessing.Manager.Queue 进行通信;

# 导入 multiprocessing 模块中的 Manager 和 Pool
from multiprocessing import Manager, Pool
import time

# 向消息队列中写入数据
def write(queue):
    # 循环从列表中取出数据写入到队列中
    for value in ["how", "are", "you"]:
        print("put %s to queue" %value)
        queue.put(value)
        time.sleep(1)

# 从消息队列中读取数据
def read(queue):
    # 根据队列中消息的数量循环读取消息
    for i in range(queue.qsize()):
        print("get %s from queue" %(queue.get()))
        time.sleep(1)

if __name__ == "__main__":
    # 主进程中创建 queue 对象,传给各个子进程
    # 不能直接使用 queue = Queue() 创建对象;
    queue = Manager().Queue()
    # 创建进程池对象
    pool = Pool()

    # 以阻塞方式启动进程池中的子进程,执行写操作
    pool.apply(write, (queue, ))
    # 以阻塞方式启动进程池中的子进程,执行读操作
    pool.apply(read, (queue, ))

    pool.close()    # 关闭进程池,不再接受新的请求
    pool.join()     # 等待进程池中所有子进程执行完毕