【从零学习python 】83. Python多进程编程与进程池的使用

2024-02-29 20:29:16 浏览数 (1)

创建进程

multiprocessing模块就是跨平台版本的多进程模块,提供了一个Process类来代表一个进程对象,这个对象可以理解为是一个独立的进程,可以执行另外的事情。

示例:创建一个进程,执行两个死循环。

代码语言:javascript复制
from multiprocessing import Process
import time

def run_proc():
    """子进程要执行的代码"""
    while True:
        print("----2----")
        time.sleep(1)

if __name__=='__main__':
    p = Process(target=run_proc)
    p.start()
    while True:
        print("----1----")
        time.sleep(1)

说明

创建子进程时,只需要传入一个执行函数和函数的参数,创建一个Process实例,用start()方法启动。

方法说明

Process(target [, name [, args [, kwargs]]])

  • target:如果传递了函数的引用,可以任务这个子进程就执行这里的代码。
  • args:给target指定的函数传递的参数,以元组的方式传递。
  • kwargs:给target指定的函数传递命名参数。
  • name:给进程设定一个名字,可以不设定。

Process创建的实例对象的常用方法:

  • start():启动子进程实例(创建子进程)。
  • is_alive():判断进程子进程是否还在活着。
  • join([timeout]):是否等待子进程执行结束,或等待多少秒。
  • terminate():不管任务是否完成,立即终止子进程。

Process创建的实例对象的常用属性:

  • name:当前进程的别名,默认为Process-NN为从1开始递增的整数。
  • pid:当前进程的pid(进程号)。

示例:

代码语言:javascript复制
from multiprocessing import Process
import os
from time import sleep

def run_proc(name, age, **kwargs):
    for i in range(10):
        print('子进程运行中,name= %s,age=%d ,pid=%d...' % (name, age, os.getpid()))
        print(kwargs)
        sleep(0.2)

if __name__=='__main__':
    p = Process(target=run_proc, args=('test',18), kwargs={"m":20})
    p.start()
    sleep(1)  # 1秒中之后,立即结束子进程
    p.terminate()
    p.join()

Pool

开启过多的进程并不能提高你的效率,反而会降低你的效率,假设有500个任务,同时开启500个进程,这500个进程除了不能一起执行之外(CPU没有那么多核),操作系统调度这500个进程,让他们平均在4个或8个CPU上执行,这会占用很大的空间。

如果要启动大量的子进程,可以用进程池的方式批量创建子进程:

代码语言:javascript复制
def task(n):
    print('{}----->start'.format(n))
    time.sleep(1)
    print('{}------>end'.format(n))

if __name__ == '__main__':
    p = Pool(8)  # 创建进程池,并指定进程池的个数,默认是CPU的核数
    for i in range(1, 11):
        # p.apply(task, args=(i,))  # 同步执行任务,一个一个地执行任务,没有并发效果
        p.apply_async(task, args=(i,))  # 异步执行任务,可以达到并发效果
    p.close()
    p.join()

进程池获取任务的执行结果:

代码语言:javascript复制
def task(n):
    print('{}----->start'.format(n))
    time.sleep(1)
    print('{}------>end'.format(n))
    return n ** 2

if __name__ == '__main__':
    p = Pool(4)
    for i in range(1, 11):
        res = p.apply_async(task, args=(i,))  # `res` 是任务的执行结果
        print(res.get())  # 直接获取结果的弊端是,多任务又变成同步的了
    p.close()
    # p.join()  不需要再`join`了,因为 `res.get()`本身就是一个阻塞方法

异步获取进程的执行结果:

代码语言:javascript复制
import time
from multiprocessing.pool import Pool

def task(n):
    print('{}----->start'.format(n))
    time.sleep(1)
    print('{}------>end'.format(n))
    return n ** 2

if __name__ == '__main__':
    p = Pool(4)
    res_list = []
    for i in range(1, 11):
        res = p.apply_async(task, args=(i,))
        res_list.append(res)  # 使用列表来保存进程执行结果
    for re in res_list: 
        print(re.get())
    p.close()

进程间不能共享全局变量

代码语言:javascript复制
from multiprocessing import Process
import os

nums = [11, 22]

def work1():
    """子进程要执行的代码"""
    print("in process1 pid=%d ,nums=%s" % (os.getpid(), nums))
    for i in range(3):
        nums.append(i)
        print("in process1 pid=%d ,nums=%s" % (os.getpid(), nums))

def work2():
    """子进程要执行的代码"""
    nums.pop()
    print("in process2 pid=%d ,nums=%s" % (os.getpid(), nums))

if __name__ == '__main__':
    p1 = Process(target=work1)
    p1.start()
    p1.join()

    p2 = Process(target=work2)
    p2.start()

    print('in process0 pid={} ,nums={}'.format(os.getpid(),nums))

运行结果:

代码语言:javascript复制
in process1 pid=2707 ,nums=[11, 22]
in process1 pid=2707 ,nums=[11, 22, 0]
in process1 pid=2707 ,nums=[11, 22, 0, 1]
in process1 pid=2707 ,nums=[11, 22, 0, 1, 2]
in process0 pid=2706 ,nums=[11, 22]
in process2 pid=2708 ,nums=[11]

0 人点赞