目录
一、进程间通信:管道(multiprocess.Pipe)
二、管道实现生产者消费模型
三、进程之间的数据共享
四、进程池和multiprocessing.Pool模块
五、回调函数
管道示意图:
子进程接收主进程消息:
from multiprocessing import Pipe,Process
def func(conn1,conn2):
conn2.close()
while True:
try :
msg = conn1.recv()
print(msg)
except EOFError:
conn1.close()
break
if __name__ == '__main__':
conn1, conn2 = Pipe()
Process(target=func,args = (conn1,conn2)).start()
conn1.close()
for i in range(20):
conn2.send('吃了么')
conn2.close()
代码解释:
EOFError:管道取完所有数后,继续取数据时所报的错误
同一个管道在主进程管道口A(conn1.close())的关闭,不会影响子进程的管道口A的接受数据(conn1.recv())
from multiprocessing import Process,Pipe,Lock
def consumer(produce, consume,name,lock):
produce.close()
while True:
lock.acquire()
baozi=consume.recv()
lock.release()
if baozi:
print('%s 收到包子:%s' %(name,baozi))
else:
consume.close()
break
def producer(produce, consume,n):
consume.close()
for i in range(n):
produce.send(i)
produce.send(None)
produce.send(None)
produce.close()
if __name__ == '__main__':
produce,consume=Pipe()
lock = Lock()
c1=Process(target=consumer,args=(produce,consume,'c1',lock))
c2=Process(target=consumer,args=(produce,consume,'c2',lock))
p1=Process(target=producer,args=(produce,consume,30))
c1.start()
c2.start()
p1.start()
produce.close()
consume.close()
进程间数据是独立的,可以借助于队列或管道实现通信,二者都是基于消息传递的
虽然进程间数据独立,但可以通过Manager实现数据共享,事实上Manager的功能远不止于此
加了锁的数据共享:
from multiprocessing import Manager,Process,Lock
def main(dic,lock):
lock.acquire()
dic['count'] -= 1
lock.release()
if __name__ == '__main__':
m = Manager()
l = Lock()
dic=m.dict({'count':100})
p_lst = []
for i in range(50):
p = Process(target=main,args=(dic,l))
p.start()
p_lst.append(p)
for i in p_lst: i.join()
print('主进程',dic)
先实例化:m = Manager()
共享的是:dic=m.dict({'count':100}) 带上m就可以共享
为什么要有进程池?进程池的概念。
在程序实际处理问题过程中,忙时会有成千上万的任务需要被执行,闲时可能只有零星任务。那么在成千上万个任务需要被执行的时候,我们就需要去创建成千上万个进程么?首先,创建进程需要消耗时间,销毁进程也需要消耗时间。第二即便开启了成千上万的进程,操作系统也不能让他们同时执行,这样反而会影响程序的效率。因此我们不能无限制的根据任务开启或者结束进程。那么我们要怎么做呢?
在这里,要给大家介绍一个进程池的概念,定义一个池子,在里面放上固定数量的进程,有需求来了,就拿一个池中的进程来处理任务,等到处理完毕,进程并不关闭,而是将进程再放回进程池中继续等待任务。如果有很多任务需要执行,池中的进程数量不够,任务就要等待之前的进程执行任务完毕归来,拿到空闲进程才能继续执行。也就是说,池中进程的数量是固定的,那么同一时间最多有固定数量的进程在运行。这样不会增加操作系统的调度难度,还节省了开闭进程的时间,也一定程度上能够实现并发效果。
Python中进程池:
python中的 先创建一个属于进程的池子
这个池子指定能存放n个进程
先讲这些进程创建好
更高级的进程池(Python中没有):
进程的上限:n,进程的下限:m 。 任务少时,进程池中有m 个进程。任务多很时,进程池中有n 个进程。可以自动调节
apply_async程序格式:
import os
import time
from multiprocessing import Pool
def func(n):
print('start func%s'%n,os.getpid())
time.sleep(1)
print('end func%s' % n,os.getpid())
if __name__ == '__main__':
p = Pool(5) 池中有5个进程
for i in range(10):
p.apply_async(func,args=(i,)) apply_async是异步执行任务
p.close() 结束进程池接收任务
p.join() 感知进程池中的任务执行结束
p.apply_async(要执行任务的函数名,args=以元祖的形式传参数)
p.close() 和 p.join() 必须紧更着后面
进程池中执行任务这步可以换成:
p.map(funcname,iterable) 默认异步的执行任务,且自带close和join
p.apply 同步调用的
p.apply_async 异步调用 和主进程完全异步 需要手动close 和 join
栗子:(有返回值的apply)
from multiprocessing import Pool
def func(i):
return i*i
if __name__ == '__main__':
p = Pool(5)
for i in range(10):
res = p.apply(func,args=(i,)) # apply的结果就是func的返回值
print(res)
0
1
4
9
16
25
36
49
64
81
apply_async中的一个参数callback = 回调函数
回调函数:
import os
from multiprocessing import Pool
def func1(n):
print('in func1', os.getpid())
return n
def func2(nn):
print('in func2', os.getpid())
print(nn)
if __name__ == '__main__':
print('主程序:', os.getpid())
p = Pool(5)
p.apply_async(func1, args=(10,), callback=func2)
p.close()
p.join()
主程序: 10832
in func1 12256
in func2 10832
10
代码解释:
先子进程中执行func1 在把执行了func1后的返回值传给func2作为参数。然后在主进程中执行func2
爬虫小栗子:
import requests
from multiprocessing import Pool
def get(url):
response = requests.get(url)
if response.status_code == 200:
return url, response.content.decode('utf-8')
def call_back(args):
url, content = args
print(url,len(content))
if __name__ == '__main__':
url_lst = [
'https://www.cnblogs.com/',
'http://www.baidu.com',
'https://www.sogou.com/',
'http://www.sohu.com/',
]
p = Pool(5)
for url in url_lst:
p.apply_async(get,args=(url,), callback=call_back)
p.close()
p.join()