上节复习、进程间通信(Piep/管道)、进程间的数据共享(Manager)、进程池(Pool/效率,返回值,回调函数) 第三十三天 2018.11.22
上节复习:
进程同步控制:
信号量 Semaphore
from multiprocessing import Semaphore
用锁的原理实现的,内置了一个计数器
在同一时间 只能有指定数量的进程执行某一段被控制住的代码
事件 Event
wait阻塞收到事件状态控制的同步组件
状态 True False is_set
true -> false clear()
false -> true set()
wait 状态为True不阻塞 状态为False的时候阻塞
锁、信号量、事件 ----> 同步控制器
进程间通信:
队列 Queue
put 当队列满的时候阻塞等待队列有空位置
get 当队列空的时候阻塞等待队列有数据
full empty 不完全准确 (可能在数据返回另一个子进程时间,队列被另一个子进程取值)
JoinableQueue
get <----> task_done
put <----> join

进程间通信---->Pipe管道:
在进程之间创建一条管道,并返回元组(conn1,conn2),其中conn1,conn2表示管道两端的连接对象---->强调一点:必须在产生Process对象之前产生管道
管道使用注意事项:
1.建立管道对象必须在产生Process对象之前
2.管道两端连接对象---->一端为发送端(send)、一端为接收端(recv)
3.需要在子进程中关闭子进程中的两端管道,子进程代码才会执行完毕
4.通过捕捉EOFError错误关闭管道接收端时---->需要把管道中的除了该进程出口外的进口出口全部关闭


from multiprocessing import Pipe,Process
# 导入管道---->注意管道必须在产生进程对象前产生
def func(conn1,conn2):
conn2.close() # 因为conn2为管道发送端---->此子进程不需要向主进程发送数据,所以不需要的关闭
while True:
try : # 当主进程发送完数据时间,msg会阻塞,当出了子进程接收数据的所有出口都关闭时间
# 子进程接收不到数据会报EOFError错误,捕获错误关闭子进程接收端管道从而使子进程代码执行完毕
msg = conn1.recv() # 阻塞---->当管道内没数据时间会阻塞
print(msg)
except EOFError: # 当管道入口都被关闭,管道出口只剩一个时间才会报错才能捕捉异常
conn1.close()
break # 退出循环,子进程代码执行完毕
if __name__ == '__main__':
conn1, conn2 = Pipe() # 管道两端的连接对象 此模块中conn1为接收端,conn2为发送端
Process(target=func,args = (conn1,conn2)).start() # 开启一个子进程对象
conn1.close() # 在主进程中关闭conn1,不影响子进程中的conn1
for i in range(20):
conn2.send('吃了么') # 主进程向子进程发送数据
conn2.close() # 主进程发送完数据后关闭掉主进程发送端
管道实现生产者消费者模型(Lock控制管道间通信):

# 通过锁来控制管道间通信
from multiprocessing import Lock,Pipe,Process
# 导入锁、管道、进程
def producer(con,pro,name,food): # 生产者
con.close() # 关闭生产者的入口---->生产者此模块不需要接收
for i in range(100):
f = '%s生产%s%s'%(name,food,i) # 谁生产了什么第几个
print(f)
pro.send(f) # 把生产者东西通过管道发送出去
pro.send(None)
pro.send(None)
pro.send(None) # 因为有三个消费者,所以需要send3个None来结束3个消费者的管道入口
pro.close() # 生产完毕关闭生产者的管道出口
def consumer(con,pro,name,lock): # 消费者
pro.close() # 消费者接收东西,不需要出口,所以关闭
while True:
lock.acquire() # 拿钥匙
food = con.recv() # 某个消费者拿到某个东西
lock.release() # 还钥匙
if food is None: # 通过判断接收的是否为None,依次关闭三个消费者的管道入口
con.close()
break
print('%s吃了%s' % (name, food)) # 不能消费者吃了None因此在break后
if __name__ == '__main__':
con,pro = Pipe() # 建立管道对象 con为管道入口、pro为管道出口
lock= Lock() # 在消费者端加锁,防止一个数据被多个消费者同时抢到
p = Process(target=producer,args=(con,pro,'egon','包子'))
c1 = Process(target=consumer, args=(con, pro, 'alex',lock))
c2 = Process(target=consumer, args=(con, pro, 'bossjin',lock))
c3 = Process(target=consumer, args=(con, pro, 'wusir',lock))
c1.start()
c2.start()
c3.start()
p.start()
con.close() # 主进程关闭管道入口
pro.close() # 主进程关闭管道出口
pipe 数据不安全性 ----> 多个消费者取一个数据 ----> 加锁
IPC(Inter-Process Communication)
加锁来控制操作管道的行为 来避免进程之间争抢数据造成的数据不安全现象
队列 进程之间数据安全的
队列 == 管道(需要每次关闭不用使用的) + 锁
进程间的数据共享(Manager):
进程间抢占资源造成的数据不安全问题

单个展示:
from multiprocessing import Manager,Process
# 进程间抢占资源造成的数据不安全问题
def main(dic):
dic['count'] -= 1
print(dic)
if __name__ == '__main__':
m = Manager()
dic=m.dict({'count':100})
p_lst = []
p = Process(target=main, args=(dic,))
p.start()
p.join()
多个展示(不加锁会造成数据错乱):
from multiprocessing import Manager,Process,Lock
def main(dic,lock):
dic['count'] -= 1
if __name__ == '__main__':
m = Manager() # 自动实现了锁的两种方法---->l.acquire() 、l.release()
l = Lock()
dic=m.dict({'count':100}) # dict为Manger对象的内置方法,可以直接操作
p_lst = []
for i in range(50): # 50个子进程在锁的作用下依次对dic进行操作
p = Process(target=main,args=(dic,l))
p.start()
p_lst.append(p)
for i in p_lst: i.join() # 等待50个子进程依次结束
print('主进程',dic)
进程池:
为什么会有进程池的概念
效率
每开启进程,开启属于这个进程的内存空间
寄存器 堆栈 文件
进程过多 操作系统的调度
进程池 个数一般情况下为cpu个数+1
python中的 先创建一个属于进程的池子
这个池子指定能存放n个进程
先讲这些进程创建好
超过5个进程用进程池
更高级的进程池 Python中没有这个,其他语言中有
n,m n下限、m上限
3 三个进程
+ 进程
20 20个
import time
from multiprocessing import Pool,Process
def func(n):
for i in range(10): # 每个进程在此循环10次
print(n+1)
if __name__ == '__main__':
start = time.time()
pool = Pool(5) # 5个进程
pool.map(func,range(100)) # 100个任务每次去5个任务到进程池 必须为可迭代
t1 = time.time() - start # 0.3280487060546875
start = time.time()
p_lst = []
for i in range(100):
p = Process(target=func,args=(i,))
p_lst.append(p)
p.start()
for p in p_lst :p.join()
t2 = time.time() - start # 5.4934046268463135
print(t1,t2)
利用os模块直观:
import os # 通过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())
# 第一次有5个任务进入进程池---->之后每结束一个任务,就有一个任务进入进程池
if __name__ == '__main__':
p = Pool(5) # 可以不带参数,默认为CPU数
for i in range(10):
# apply同步
p.apply_async(func,args=(i,)) # 异步主进程结束后不等待子进程
p.close() # 结束进程池接收任务
p.join() # 感知进程池中的任务执行结束
进程池server_client
server端:
import socket
from multiprocessing import Pool
def func(conn):
conn.send(b'hello')
print(conn.recv(1024).decode('utf-8'))
conn.close()
if __name__ == '__main__':
p = Pool(5) # 进程池设置为5个进程
sk = socket.socket()
sk.bind(('127.0.0.1',8080))
sk.listen() # server端只需要建立一次套接字对象、绑定机器端口、监听
while True:
conn, addr = sk.accept() # 建立连接
p.apply_async(func,args=(conn,)) # 建立异步连接对象最多同时5个,每执行完一个可以再有一个
sk.close()
client端:
import socket
sk = socket.socket()
sk.connect(('127.0.0.1',8080))
ret = sk.recv(1024).decode('utf-8')
print(ret)
msg = input('>>>').encode('utf-8')
sk.send(msg)
sk.close()
进程池中的方法:
p.map(funcname,iterable) 默认异步的执行任务,且自带close和join
p.apply 同步调用的
p.apply_async 异步调用 和主进程完全异步 需要手动close 和 join
进程池中的返回值:
同步:
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)
异步:
import time
from multiprocessing import Pool
def func(i):
time.sleep(0.5)
return i*i
if __name__ == '__main__':
p = Pool(5)
res_l = []
for i in range(10):
res = p.apply_async(func,args=(i,)) # apply的结果就是func的返回值
res_l.append(res)
for res in res_l:print(res.get())# 等着 func的计算结果
map方法:
import time
from multiprocessing import Pool
def func(i):
time.sleep(0.5)
return i*i
if __name__ == '__main__':
p = Pool(5)
ret = p.map(func,range(100)) # 把所有的执行完后,统一放入ret中
# map自带close和join
print(ret)
进程池的回调函数:
# 回调函数
import os
from multiprocessing import Pool
def func1(n):
print('in func1',os.getpid())
return n**2
def func2(nn): # 回调函数在主进程执行 回主进程,再调用func2
print('in func2',os.getpid())
print(nn) # 回调函数的参数为func1的返回值,,10时nn为10, 1000时nn为1000
if __name__ == '__main__':
print('主进程 :',os.getpid())
p = Pool(5)
for i in range(10):
p.apply_async(func1,args=(10,),callback=func2) # callback = func2回调func2函数
p.close()
p.join()
浙公网安备 33010602011771号