python multiprocessing模块_Python的多进程模块multiprocessing

af5cbd10b976

众所周知,Python中不存在真正的多线程,Python中的多线程是一个并发过程。如果想要并行的执行程序,充分的利用cpu资源(cpu核心),还是需要使用多进程解决的。其中multiprocessing模块应该是Python中最常用的多进程模块了。

创建进程

基本上multiprocessing这个模块和threading这个模块用法是相同的,也是可以通过函数和类创建进程。

""" 案例1:函数式创建进程 """

import multiprocessing

import time

# 进程执行函数

def run(num):

time.sleep(1)

print(f'i am process{num}')

if __name__ == '__main__':

# 获取开始的时间戳

start = time.time()

# 存放进程的列表,用于阻塞进程

process_list = list()

# 创建4个进程,并将每个创建好的进程对象放到process_list中

for i in range(1, 5):

process = multiprocessing.Process(target=run, args=(i,))

# 启动该进程

process.start()

process_list.append(process)

# 只有进程全部结束,再向下执行

for j in process_list:

j.join()

# 结束的时间戳

end = time.time()

# 打印该程序运行了几秒

print(end-start)

# i am process1

# i am process2

# i am process3

# i am process4

# 1.122110366821289

上述案例基本上就是笔者搬用了上篇文章多线程的案例,可见其使用的相似之处。导入multiprocessing后实例化Process就可以创建一个进程,参数的话也是和多线程一样,target放置进程执行函数,args存放该函数的参数。

""" 案例2:类继承创建进程 """

import multiprocessing

import time

# 继承Process,转变为进程类

class MyProcess(multiprocessing.Process):

def __init__(self, process_id):

# 必须实现Process类的init方法

super().__init__()

self.process_id = process_id

# 重写执行函数

def run(self):

time.sleep(1)

print(f'i am process{self.process_id}')

if __name__ == '__main__':

# 获取开始的时间戳

start = time.time()

# 存放进程的列表,用于阻塞进程

process_list = list()

# 创建4个进程,并将每个创建好的进程对象放到process_list中

for i in range(1, 5):

process = MyProcess(i)

# 启动该进程

process.start()

process_list.append(process)

# 只有进程全部结束,再向下执行

for j in process_list:

j.join()

# 结束的时间戳

end = time.time()

# 打印该程序运行了几秒

print(end - start)

# i am process1

# i am process2

# i am process3

# i am process4

# 1.1184189319610596

使用类来创建进程也是需要先继承multiprocessing.Process并且实现其init方法。

进程池

Pool可以提供指定数量的进程,供用户调用,当有新的请求提交到pool中时,如果池还没有满,那么就会创建一个新的进程用来执行该请求。

但如果池中的进程数已经达到规定最大值,那么该请求就会等待,直到池中有进程结束,才会创建新的进程。

""" 案例3:进程池Pool """

import multiprocessing

import time

# 进程执行函数

def func(msg):

print('process start...', msg)

time.sleep(3)

print('process end...')

if __name__ == '__main__':

print('ready~~~~~~~~~~~~~~~~~go~~~~~')

# 创建进程池,最大进程数量为3

pool = multiprocessing.Pool(processes=3)

for i in range(5):

msg = f'hello {i}'

# 以非阻塞的方式,维持执行的进程总数为processes并在进程结束后自动添加新的进程

pool.apply_async(func, (msg,))

# 阻止后续任务提交到进程池

pool.close()

# 等待工作进程结束

pool.join()

print('game~~~~~~~~~~~~~~~~~over~~~~~')

# ready~~~~~~~~~~~~~~~~~go~~~~~

# process start... hello 0

# process start... hello 1

# process start... hello 2

# process end...

# process start... hello 3

# process end...

# process end...

# process end...

# game~~~~~~~~~~~~~~~~~over~~~~~

需要注意的是,在调用join方法阻塞进程前,需要先调用close方法,,否则程序会出错。

在上述案例中,提到了非阻塞,当把创建进程的方法换为pool.apply(func, (msg,))时,就会阻塞进程,出现下面的状况。

# ready~~~~~~~~~~~~~~~~~go~~~~~

# process start... hello 0

# process end...

# process start... hello 1

# process end...

# process start... hello 2

# process end...

# process start... hello 3

# process end...

# process start... hello 4

# process end...

# game~~~~~~~~~~~~~~~~~over~~~~~

进程队列

在multiprocessing模块中还存在Queue对象,这是一个进程的安全队列,近似queue.Queue。队列一般也是需要配合多线程或者多进程使用。

下列案例是一个使用进程队列实现的生产者消费者模式。

""" 案例4:基于进程队列的生产者消费者模式 """

import random

import time

import multiprocessing

# 生产者

def producer(queue):

for i in range(10):

time.sleep(random.randint(1, 3))

res = f'物品{i}'

# 入队

queue.put(res)

print(f'{multiprocessing.current_process().name}生产{res}')

# 消费者

def consumer(queue):

while True:

# 出队

res = queue.get()

time.sleep(random.randint(1, 3))

print(f'{multiprocessing.current_process().name}消费{res}')

if __name__ == '__main__':

# 生产消费队列

queue = multiprocessing.Queue()

# 创建生产进程

process1 = multiprocessing.Process(target=producer, args=(queue,))

# 进程名

process1.name = 'process_1'

# 创建消费进程

process2 = multiprocessing.Process(target=consumer, args=(queue,))

# 进程名

process2.name = 'process_2'

print('程序开始!!!')

# 启动进程

process1.start()

process2.start()

# 程序开始!!!

# process_1生产物品0

# process_1生产物品1

# process_2消费物品0

# process_1生产物品2

# process_2消费物品1

# process_1生产物品3

# ......

管道

multiprocessing支持两种进程间的通信,其中一种便是上述案例的队列,另一种则称作管道。在官方文档的描述中,multiprocessing中的队列是基于管道实现的,并且拥有更高的读写效率。

管道可以理解为进程间的通道,使用Pipe([duplex])创建,并返回一个元组(conn1,conn2)。如果duplex被置为True(默认值),那么该管道是双向的,如果duplex被置为False,那么该管道是单向的,即conn1只能用于接收消息,而conn2仅能用于发送消息。

其中conn1、conn2表示管道两端的连接对象,每个连接对象都有send()和recv()方法。send和recv方法分别是发送和接受消息的方法。例如,可以调用conn1.send发送消息,conn1.recv接收消息。如果没有消息可接收,recv方法会一直阻塞。如果管道已经被关闭,那么recv方法会抛出EOFError。

""" 案例5:管道 """

import multiprocessing

import random

import time

# 发送数据

def proc_send(pipe, data):

for d in data:

pipe.send(d)

print(f'{multiprocessing.current_process().name}发送了数据{d}')

time.sleep(random.random())

# 接收数据

def proc_recv(pipe):

while True:

print(f'{multiprocessing.current_process().name}接收了数据{pipe.recv()}')

time.sleep(random.random())

if __name__ == '__main__':

# 创建管道

pipe1, pipe2 = multiprocessing.Pipe()

# 数据

data = [i for i in range(10)]

# 创建进程process1

process1 = multiprocessing.Process(target=proc_send, args=(pipe1, data))

process1.name = 'process_1'

# 创建进程process1

process2 = multiprocessing.Process(target=proc_recv, args=(pipe2,))

process2.name = 'process_2'

# 启动进程

process1.start()

process2.start()

# 阻塞进程

process1.join()

process2.join()

# process_1发送了数据0

# process_2接收了数据0

# process_1发送了数据1

# process_2接收了数据1

# process_1发送了数据2

# process_2接收了数据2

# process_1发送了数据3

# process_2接收了数据3

# ......

关于multiprocessing模块其实还有很多实用的类和方法,由于篇幅有限(懒),笔者就先写到这里。该模块其实用起来很像threading模块,像锁对象和守护线程(进程)等multiprocessing模块也是有的,使用方法也近乎相同。

如果想要更加详细的了解multiprocessing模块,请参考官方文档。

# multiprocessing --- 基于进程的并行

https://docs.python.org/zh-cn/3/library/multiprocessing.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值