every blog every motto: Light tomorrow with today.

0. 前言

网上目前关于多进程返回值的文章较为零散,本文主要进行简单的小结。
说明:

  • 其中被测试函数的函数返回值作为函数参数。所以使用多进程运行时间并没有减少,反而更慢,这是需要说明的,关于运行时间,仅作一般结果进行展示,不是本文的重点。
  • 其中关于apply和apply_async两种方法运行时间的比较可参考时间比较

1. 正文

测试函数时间用到装饰器,具体可参考装饰器


导入模块:

import threading
from queue import Queue
import multiprocessing
from multiprocessing import Manager
import time

loop_number = 30000 # 循环次数

1.1 无多进程情况

def fun(k):
    """被测试函数"""
    print('-----fun函数,参数为{}----'.format(k))
    m = k + 10
    return m
@count_time
def call_fun():
    """没有使用多线/进程的情况"""
    number = 0

    for i in range(loop_number):
        print('number:{}'.format(number))
        number = fun(number)

def main():
    """主程序"""

    # 1. 没有使用多线程
    call_fun()


if __name__ == '__main__':
    main()

结果:

Python获取多进程返回值 python 进程返回值_返回结果

1.2 多进程返回值

1.2.1 方法一:进程池Pool

有关多种进程池的apply,apply_async的时间比较可参考文献1

1. apply
def fun(k):
    """被测试函数"""
    print('-----fun函数,参数为{}----'.format(k))
    m = k + 10
    return m  
   
@count_time
def my_process():
    """多进程"""

    # 方法一:apply/apply_async
    pool = multiprocessing.Pool(4)  # 创建4个进程
    k = 0
    for i in range(loop_number):
        k = pool.apply(fun, args=(k,)
        print('返回值为:', k)

 
def main():
    """主程序"""

    # 3. 使用多进程
    my_process()


if __name__ == '__main__':
    main()

结果:

Python获取多进程返回值 python 进程返回值_运行时间_02

2. apply_async
def fun(k):
    """被测试函数"""
    print('-----fun函数,参数为{}----'.format(k))
    m = k + 10
    return m  
   
@count_time
def my_process():
    """多进程"""

    # 方法一:apply/apply_async
    pool = multiprocessing.Pool(4)  # 创建4个进程
    k = 0
    for i in range(loop_number):
        k = pool.apply_async(fun, args=(k,))
        k = k.get()
        print('返回值为:', k)

 
def main():
    """主程序"""

    # 3. 使用多进程
    my_process()


if __name__ == '__main__':
    main()

结果:

Python获取多进程返回值 python 进程返回值_返回结果_03


说明:

上述运行时间,仅作一般参考,运行时间非本文重点,具体参考文献1小结:

  1. 因为测试函数的特殊性(函数返回值作为参数传递进函数),两种方法都相当于串行,所以时间方面并没差距
  2. apply_async方法返回结果为对象,所以需要用get方法获取结果

apply_async关于返回结果位置小讨论:
1. 返回结果中间输出:
@count_time
def my_process():
    """多进程"""

    # 方法一:apply/apply_async
    pool = multiprocessing.Pool(4)  # 创建4个进程
    k = 0
    for i in range(loop_number):
        t = pool.apply_async(fun, args=(k,))

        print('返回值为:', t.get())

结果:

Python获取多进程返回值 python 进程返回值_Python获取多进程返回值_04

2. 返回结果最后输出:
@count_time
def my_process():
    """多进程"""

    # 方法一:apply/apply_async
    pool = multiprocessing.Pool(4)  # 创建4个进程
    k = 0
    li = [] # 空列表
    for i in range(loop_number):
        t = pool.apply_async(fun, args=(k,))
		li.append(t)
	for i in li:
        print('返回值为:', i.get())

结果:

Python获取多进程返回值 python 进程返回值_Python获取多进程返回值_05


时间不同的原因:

在进行得到各个结果的时候,是将结果追加到列表中,否则,在得到结果get的时候会阻塞进程,从而将多进程变成了单进程,当使用列表来存放时,在进行get数据的时候,可以设置超时时间,如,get(timeout=5)。

小结:

  • 对于apply_async返回结果最后输出时间更短
  • 对于apply二者没有明显区别,读者可自行验证

1.2.2 方法二:Manger

from multiprocessing import Manager, Process

def fun(k,result_dict):
    """被测试函数"""
    print('-----fun函数内部,参数为{}----'.format(k))
    m = k + 10

    result_dict[k] = m # 方法二:manger

@count_time
def my_process():
    """多进程"""

    # 方法二:Manger
    manger = Manager()
    result_dict = manger.dict()  # 使用字典
    jobs = []

    for i in range(10):
        p = Process(target=fun, args=(i, result_dict))
        jobs.append(p)
        p.start()

    for pr in jobs:
        pr.join()
    var = result_dict
    print('返回结果', var.values())

def main():
    """主程序"""


    # 3. 使用多进程
    my_process()


if __name__ == '__main__':
    main()

结果:

Python获取多进程返回值 python 进程返回值_Python获取多进程返回值_06

说明:

  • 上面用的是字典,也可以用列表
  • 返回的结果只有等所有子进程运行完以后才能获取,从这一点上来说,不满足我们的要求,由于作为获取多进程的一种方法,附上。

1.2.3 方法三:Pipe

定义类

class MyProcess(multiprocessing.Process):
    def __init__(self, name, func, args):
        super(MyProcess, self).__init__()
        self.name = name
        self.func = func
        self.args = args
        self.res = ''

    def run(self):
        self.res = self.func(*self.args)
def fun(k,p):
    """被测试函数"""
    print('-----fun函数内部,参数为{}----'.format(k))
    m = k + 10

    p.send(m)

@count_time
def my_process():
    """多进程"""

    # 方法三:
    process_li = []
    parent_con, child_con = multiprocessing.Pipe()
    for i in range(30):
        p = MyProcess('proce', fun, (1, child_con))
        process_li.append(p)
        p.start()
    for i in process_li:
        i.join()
    for i in process_li:
        print(parent_con.recv())

结果:

Python获取多进程返回值 python 进程返回值_返回结果_07


说明:

  • 当参数loop_number=30000时,内存溢出,电脑被搞黑屏了,所以仅循环30次,
  • 函数返回的结果只能最后一起输出,不符合要求。

1.2.3 方法四:Quene

定义类

class MyProcess(multiprocessing.Process):
    def __init__(self, name, func, args):
        super(MyProcess, self).__init__()
        self.name = name
        self.func = func
        self.args = args
        self.res = ''

    def run(self):
        self.res = self.func(*self.args)
def fun(k, p):
    """被测试函数"""
    print('-----fun函数内部,参数为{}----'.format(k))
    m = k + 10

    p.put(m)

@count_time
def my_process():
    """多进程"""

    # 方法四:Queue
    process_li = []
    q = multiprocessing.Queue()
    k = 1
    for i in range(30):
        p = MyProcess('proce', fun, (k, q))
        p.start()
        process_li.append(p)
    for i in process_li:
        i.join()

    while q.qsize() > 0:
        print(q.get())

结果:

Python获取多进程返回值 python 进程返回值_运行时间_08


说明:

  • 和上面Pipe一样,内存溢出,其结果只能放在队列当中,只能最后取得,不满足需求

总结:

  • 建议使用进程池apply / apply_async
  • 参照这篇文章apply/apply_async时间比较分析,推荐使用ThreadPool下的apply_async