Python教程:多进程与进程池的用法

一、进程:

一个程序运行起来后,代码+用到的资源称之为进程,它是操作系统分配资源的基本单元。其也会有并发与并行的状态,与多线程不同的是,多进程会充分利用CPU的资源来执行任务。

进程的状态:
当任务数大于CPU的核数时,部分任务在执行,部分任务在等待执行,因此就会产生不通的状态:
1.就绪状态:运行条件以满足,等待CPU执行。
2.执行状态:CPU正在执行其任务。
3.等待状态:等待某些条件满足,例如一个程序sleep了。

multiprocessing模块
python中的多线程其实并不是真正的多线程,如果想要充分地使用多核CPU的资源,在python中大部分情况需要使用多进程。Python提供了非常好用的多进程包multiprocessing,其支持子进程、通信和共享数据、执行不同形式的同步,提供了Process、Queue、Pipe、Lock等组件

Process:
使用方法与线程Thread类一样如下,需要注意的是进程的创建与启动必须放在__main__下面,否则会造成循环加载任务代码的问题。

from multiprocessing import Process

a = 0

def work(name):
    global a
    for i in range(100):
        a += 1
    print(f"{name}----", a)


def work1(name):
    global a
    for i in range(100):
        a += 1
    print(f"{name}----", a)


if __name__ == '__main__':
    p = Process(target=work, args=('MING',)) # 创建进程对象,通过args传入任务参数
    p1 = Process(target=work1,args=('CHAZ',),daemon=True) #设置进程是否为守护进程,如果是守护进程,则与主进程一起关闭。
    p.start() # 启动进程
    p1.start()
    p.join() # 等待进程执行完成
    p1.join()
    print('a:', a)

>>>MING---- 100
>>>CHAZ---- 100
>>>a: 0

os.getpid()查看进程id,os.getppid(),查看父进程id

import time,os
from multiprocessing import Process


def fun():
    time.sleep(2)
    print(f"fun--进程id{os.getpid()},父进程id{os.getppid()}")


def fun1():
    time.sleep(2)
    print(f"fun1--进程id{os.getpid()},父进程id{os.getppid()}")


if __name__ == '__main__':
    p = Process(target=fun)
    p1 = Process(target=fun1)
    p.start()
    p1.start()
    p.join()
    p1.join()
    print(f'主进程--{os.getpid()}')
 
>>> fun--进程id18188,父进程id14604
>>> fun1--进程id19256,父进程id14604
>>> 主进程--14604

从例1中可以看到进程之间的信息是独立的,并没有共享,如果想实现进程之间的通信可以使用Queue模块,(需要注意的是并非queue模块中的Queue,此队列只能用来在一个进程中的多个线程之间使用)。而进程中的Queue可以在多个进程之间跨进程传输数据。
导入:

from multiprocessing import Queue

举例:
一个列表中有10个url地址,每个地址请求一次,使用2个进程程去发送这 10个请求(假设请求每个地址需要0.5秒)
在不使用Queue的情况下:

from multiprocessing import Process
import time

li = ['www.baidu.com-{}'.format(i) for i in range(10)]


def work():
    while li:
        url = li.pop()
        print("请求地址:", url)
        time.sleep(0.5)


if __name__ == '__main__':
    t = Process(target=work)
    t1 = Process(target=work)
    t.start()
    t1.start()
    t.join()
    t1.join()

运行结果:

请求地址: www.baidu.com-9
请求地址: www.baidu.com-9
请求地址: www.baidu.com-8
请求地址: www.baidu.com-8
请求地址: www.baidu.com-7
请求地址: www.baidu.com-7
请求地址: www.baidu.com-6
.....

此时每个进程拿到的列表是独立的,并不是共享的才造成上面的问题。如下,使用进程中的队列解决这一问题:

from multiprocessing import Process, Queue
import time

def work(urls):
    while not urls.empty(): # 判断队列是否为空
        url = urls.get() # 获取队列中的值
        print("请求地址:", url)
        time.sleep(0.5)


if __name__ == '__main__':
    urls = Queue() # 创建队列
    for i in range(10):
        urls.put(f"www.baidu.com-{i}") # 队列中添加数据
    t = Process(target=work, args=(urls,)) # 创建进程对象,将队列作为函数的参数
    t1 = Process(target=work, args=(urls,))
    t.start()
    t1.start()
    t.join()
    t1.join()

运行结果:

请求地址: www.baidu.com-0
请求地址: www.baidu.com-1
请求地址: www.baidu.com-2
请求地址: www.baidu.com-3
请求地址: www.baidu.com-4
请求地址: www.baidu.com-5
....

二、进程池

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

创建进程池时候可以指定最大进程数,当有新的请求要提交到Pool中时,会判断Pool中有没有达到最大进程数,如果达到,该请求就会等待,直到池中有进程结束,才会用之前的进程来执行新的任务。
进程池之间的通讯使用Manager中的Queue()。

from multiprocessing import Pool
import time

def func(msg):
    print("msg:", msg)
    time.sleep(3)

if __name__ == "__main__":
    po = Pool(3) # 创建进程池
    for i in range(4):
        msg = f"hello{i}"
        po.apply_async(func, (msg,))  # 维持执行的进程总数为processes,当一个进程执行完毕后会添加新的进程进去

    print("Mark~ Mark~ Mark~~~~~~~~~~~~~~~~~~~~~~")
    po.close()
    po.join()  # 调用join之前,先调用close函数,否则会出错。执行完close后不会有新的进程加入到pool,join函数等待所有子进程结束
    print("Sub-process(es) done.")

ProcessPoolExecutor:与线程池ThreadPoolExecutor 原理及使用方法是一样的,通过ProcessPoolExecutor创建线程池,submit提交任务到线程池。max_workers指定线程池中的数量

import time
from concurrent.futures import ProcessPoolExecutor


def work(name):
    for i in range(5):
        time.sleep(1)
        print(f"{name}----{i}----")

#学习中遇到问题没人解答?小编创建了一个Python学习交流群:711312441
with ProcessPoolExecutor(max_workers=4) as tp: # 创建进程池
    tp.submit(work, 'MING') # 往进程提交任务
    tp.submit(work, 'HOUX')
	tp.shutdown() # 等待进程池中所有的任务执行完成
    print("---主进程---")

运行结果:

MING----0----HOUX----0----

MING----1----HOUX----1----

HOUX----2----MING----2----

MING----3----HOUX----3----

HOUX----4----MING----4----

---主进程---
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值