进程---进程间通信 --进程之间数据分享--进程池

进程间通信

IPC

队列

1,正常队列

 

from queue import Queue
先进先出FIFO ----维护秩序时用的比较多
q = Queue() ##q是一个队列
print(q)
q.put(12) ##q.put 是往q里面放东西
print(q.get()) ## q.get() 是拿q里面的东西
print(q.qsize()) ##队列的长度

栈:先进后出 ----算法用的比较多
用栈实现三级菜单
计算文件夹的总大小

print(q.get()) ##没有值得时候回阻塞
print(q.get_nowait()) ##当有值的时候取值
print(q.get_nowait()) #没有值的时候会报错


进程中的队列
from multiprocessing import Queue

创建共享的进程队列,Queue是多进程安全的队列,可以使用Queue实现多进程之间的数据传递
q.empty()
判断队列是否为空,返回布尔值
q.full()
判断队列是否满了,返回布尔值


主进程放,子进程取
from multiprocessing import Queue,Process
def con(q):
print(q.get())

if __name__ == '__main__':
q = Queue()
p = Process(target=con,args=(q,))
p.start()
q.put(123)

得到打印结果 : 123


子进程放,另一个子进程取
from multiprocessing import Queue,Process
def con(q):
print(q.get())

def pro(q):
q.put(123)

if __name__ =='__main__':
q = Queue()
p = Process(target=con,args=(q,))
p.start()
p= Process(target=pro,args=(q,))
p.start()


生产者消费者模型
生产者消费者模型 --解决创造(生产)数据和处理(消费)数据的效率不平衡问题
把创造数据 和 处理数据放在不同的进程中,
根据他们的效率在调整进程的个数

生产数据快,消费数据慢,内存空间的浪费
消费数据快,生产数据慢,效率低下
import time
import random
from multiprocessing import Process,Queue

def consumer(q,name):
while True:
food = q.get()
if food == 'stop':break
print('%s 吃了 %s'%(name,food))
time.sleep(random.random())

def producer(q,name,food,n=10):
for i in range(n):
time.sleep(random.random())
fd = food+str(i)
print('%s 生产了 %s' %(name,fd))
q.put(fd)

if __name__ =='__main__':
q = Queue(10)
c1 = Process(target=consumer,args=(q,'alex'))
c1.start()
c2 = Process(target=consumer,args=(q,'alex'))
c2.start()
p1 = Process(target=producer,args=(q,'太白','泔水'))
p1.start()
p2 = Process(target=producer,args=(q,'egon','鱼刺'))
p2.start()
p1.join()
p2.join()
q.put('stop')
q.put('stop')
让consumer停下来的方法
在所有生产者结束生产之后,向队列中放入一个结束符
有几个consumer就向队列中放几个结束符
在消费者消费的过程中,接收到结束符,就结束消费的进程


Joinable Queue
创建可连接的共享进程队列,这就像是一个Queue对象,但队列润许项目的使用者通知生产者项目已被
成功处理.通知进程是使用共享信号和条件变量来实现的
import time
import random
from multiprocessing import JoinableQueue,Process
def consumer(q,name):
while True:
food = q.get()
print('%s 吃了 %s'%(name,food))
time.sleep(random.random())
q.task_done()

def producer(q,name,food,n=10)
for i in range(n):
time.sleep(random.random())
fd = food+str(i)
print('%s 生产了 %s'%(name,fd))
q.put(fd)
q.join()

if __name__ =='__main__':
q = JoinableQueue()
c1 = Process(target=consumer,args=(q,'alex'))
c1.daemon = True
c1.start()
c2 = Process(target=consumer,args=(q,'alex'))
c2.daemon = True
c2.start()
p1 = Process(target=producer,args=(q,'太白','泔水'))
p1.start()
p2 = Process(target=producer,args=(q,'egon','鱼刺'))
p2.start()
p1.join()
p2.join()

只有multiprocessing 中的队列 才能帮助你 实现 IPC
永远不可能出现数据不安全的情况,多个进程不会同时取走同一个数据
提供给的方法
put
get
put_nowwait
get_nowwait
empty --- 在多进程内不可靠
full --- 在多进程内不可能
qsize --- 在多进程内不可靠

有图先进先出的特点 + 进程通信的功能 +数据进程安全,经常用它来完成进程之间的通信
生产者消费者模型
生产者和消费者的效率平衡的问题
内存的控制 - 队列的长度限制
让消费者自动停下来

joinableQueue
在消费数据的时候 task_done
在生产端\主进程 join


管道
队列就是基于管道实现的
队列 数据安全的
管道 数据不安全的
队列 = 管道 + 锁

#管道初使用
from multiprocessing import Pipe
left,right = Pipe()
left.send('aaa')
print(right.recv())
#创建管道的类
pipe([duplex]):在进程之间创建一条管道,并返回元组(conn1,conn2),其中conn1,conn2表示管道两端的连接对象,强调一点:必须在产生Process对象之前产生管道

#参数介绍:
dumplex:默认管道是全双工的,如果将duplex射程False,conn1只能用于接收,conn2只能用于发送

#主要方法:
        conn1.recv():接收conn2.send(obj) 发送的对象.如果没有消息可接收,recv方法会一直阻塞.如果连接的另外一端已经关闭,那么recv方法会跑出EOFError.
        conn1.send(obj);通过连接发送对象.obj是与序列化兼容的任意对象

#其他方法:
conn1.close():关闭连接。如果conn1被垃圾回收,将自动调用此方法
conn1.fileno():返回连接使用的整数文件描述符
conn1.poll([timeout]):如果连接上的数据可用,返回True。timeout指定等待的最长时限。如果省略此参数,方法将立即返回结果。如果将timeout射成None,操作将无限期地等待数据到达。
 
conn1.recv_bytes([maxlength]):接收c.send_bytes()方法发送的一条完整的字节消息。maxlength指定要接收的最大字节数。如果进入的消息,超过了这个最大值,将引发IOError异常,并且在连接上无法进行进一步读取。如果连接的另外一端已经关闭,再也不存在任何数据,将引发EOFError异常。
conn.send_bytes(buffer [, offset [, size]]):通过连接发送字节数据缓冲区,buffer是支持缓冲区接口的任意对象,offset是缓冲区中的字节偏移量,而size是要发送字节数。结果数据以单条消息的形式发出,然后调用c.recv_bytes()函数进行接收    
 
conn1.recv_bytes_into(buffer [, offset]):接收一条完整的字节消息,并把它保存在buffer对象中,该对象支持可写入的缓冲区接口(即bytearray对象或类似的对象)。offset指定缓冲区中放置消息处的字节位移。返回值是收到的字节数。如果消息长度大于可用的缓冲区空间,将引发BufferTooShort异常。
介绍

 

##EOFError##
#在这一个进程中 如果不在用这个断电了,应该close
#这样在recv的时候,如果其他断电都被关闭了,就能够知道不会再有新的消息传进来
#此时就不会在这里阻塞等待,而是跑出一个EOFError
# close 并不是关闭了整个管道,而是修改了操作系统对管道端点的引用计数的处理


from multiprocessing import Pipe,Process
def consumer(left,right):
    left.close()
    while True:
        try:
            print(right.recv())
        except EOFError:
            break

if __name__ == '__main__':
    left,right = Pipe()
    p = Process(target=consumer,args=(left,right))
    p.start()
    right.close()
    for i in range(10):
        left.send('hello')
    left.close()
EOFError 异常触发

管道是由操作系统进行引用计数的,必须在所有进程中关闭管道后才能生成EOFError异常

from multiprocessing import Process,Pipe

def consumer(p,name):
    produce, consume=p
    produce.close()
    while True:
        try:
            baozi=consume.recv()
            print('%s 收到包子:%s' %(name,baozi))
        except EOFError:
            break

def producer(p,seq=10):
    produce, consume=p
    consume.close()
    for i in range(seq):
        produce.send(i)

if __name__ == '__main__':
    produce,consume=Pipe()
    for i in range(5):
        c=Process(target=consumer,args=((produce,consume),'c1'))
        c.start()
    for i in range(5):
        p = Process(target=producer, args=((produce, consume)))
        p.start()
    producer((produce,consume))
    produce.close()
    consume.close()
Pipe实现生产着消费者模型

进程之间的数据共享

子进程可以用住进程里面的东西,想用就用,不想用就不用

from multiprocessing import Manager,Process,Lock
def work(d,lock):
    with lock:          #不枷锁的话 容易造成数据错乱
        d['count']-=1

if __name__ == '__main__':
    lock = Lock()
    m = Manager()
    dic=m.dict({'count':100})
    p_l=[]
    for i in range(100):
        p=Process(target=work,args=(dic,lock))
        p_l.append(p)
        p.start()
    for p in p_l:
        p.join()
    print(dic)

  进程池

 

进程池的概念:

  定义一个池子,在里面放上固定数量的进程,有需求来了,就拿池中的进程来处理任务,等到处理完毕,进程并不关闭

,而是将进程再放会进程池中继续等待任务.如果有很多任务需要执行,池中的进程数量不够,任务就要等待之前的进程

执行完毕,才能继续执行,就是说,池中的进程数量是固定的,那么同一时间最多有固定数量的进程在执行,这样不会增加

系统的调度难度,还节省开闭进程的时间,也一定程度上能够实现并发效果.

什么时候用进程池?

  面向高计算型的场景,采用多进程

  如果开启的进程数超过5个

  有几个CPU就能够同时运行几个进程

进程的弊端:开启和关闭以及切换都会带来很大的时间开销过多的进程还会造成操作系统调度的压力

import os
import time
from multiprocessing import Pool
def func(i):
    time.sleep(0.4)
    print(os.getpid(),i)

if __name__ =='__main__':
    p = Pool(5)           #有5个进程数量
    for i in range(20):   #20个等待执行
        p.apply_async(func,args= (i,))
    p.close()
    p.join()
创建一个简单的进程池

 

同步和异步 (进程池)
##同步请求的
# import os
# import time
# from multiprocessing import Pool
# ##同步请求的
# def wahaha():
# time.sleep(1)
# print(os.getpid())
# return True
#
# if __name__ =='__main__':
# p = Pool(5)
# ret_l = []
# for i in range(10):
# ret = p.apply(func = wahaha)
# print(ret)

# ##异步提交,不获取返回值
# import time
# import os
# from multiprocessing import Process,Pool
# def wahaha():
# time.sleep(1)
# print(os.getpid())
#
# if __name__ =='__main__':
# p = Pool(5) ##cpu的个数 ,或者 +1
# ret_l = []
# for i in range(20):
# ret = p.apply_async(func = wahaha) ##async 异步的
# ret_l.append(ret)
# p.close() ##关闭,进程池中的进程不工作了
# ##而是关闭了进程池,让人物不能再继续提交了
# p.join() ##等待这个池中提交的人物都执行完
# ##表示等待所有子进程中的代码都执行完, 主进程才结束


# ##异步提交,获取返回值,等待所有任务都执行完毕之后再统一获取结果(获取值,全部获取完以后获得返回值)
# import time
# import os
# from multiprocessing import Process,Pool
# def wahaha():
# time.sleep(1)
# print(os.getpid())
# return True
#
# if __name__=='__main__':
# p = Pool(5) ##cpu的个数,或者+1
# ret_l = []
# for i in range(20):
# ret = p.apply_async(func = wahaha) ##async 异步的
# ret_l.append(ret)
# p.close() ##关闭,进程池中的进程不工作了
# ##而是关闭了进程池,让人物不能再继续提交了
# p.join() ## 等待这个池中提交的任务都执行完
# for ret in ret_l:
# print(ret.get())

##异步提交,获取返回值,一个任务执行完毕之后就可以获取到一个结果
import time
import os
from multiprocessing import Process,Pool
def wahaha():
time.sleep(1)
print(os.getpid())
return True

if __name__ == '__main__':
p = Pool(5)
ret_l = []
for i in range(20):
ret = p.apply_async(func=wahaha) #async 异步的
ret_l.append(ret)
for ret in ret_l:
print(ret.get())

##异步的 apply_async
##1.如果是异步的提交任务,那么任务提交之后进程池和主进程也异步了
# 主进程不会自动等待进程池中的任务执行完毕
##2.如果需要主进程等待,需要p.join
# 但是join的行为是依赖close
##3.如果这个函数是有返回值的
## 也可以通过ret.get()来获取返回值
## 但是如果一边提交一边获取返回值会让程序变成同步的
## 所以要想保留异步的效果,应该讲返回对象保存在列表里,所有任务提交完成之后再来取结果
## 这种方式也可以去掉join,来完成主进程的阻塞等待池中的任务执行完毕


###############回调函数##########
##回调函数 _ 在主进程中执行
##在发起任务的时候 指定callback参数
##在每个进程执行完apply_async任务之后,返回值会直接作为参数传递给callback的函数,执行callback
##函数中的代码
import os
import time
import random
from multiprocessing import Pool

def wahaha(num):
time.sleep(random.random())
print('pid:',os.getpid(),num)
return num

def back(arg):
print('call_back:',os.getpid(),arg)

if __name__=='__main__':
print('主进程',os.getpid())
p = Pool(5)
for i in range(20):
ret = p.apply_async(func = wahaha,args = (i,),callback=back) ##async 异步的
p.close()
p.join()

总结 

进程
进程三状态
同步异步阻塞非阻塞
请解释异步非阻塞
给开发完成的所有装饰器+log
是计算机中最小的资源分配单位
进程的创建 Process
进程之间的异步 本身子进程主进程之间都是异步的
进程之间的同步控制 Lock Semaphore Event
进程之间的数据隔离 本身进程与进程之间都是数据隔离的
进程之间通信 IPC 管道 队列
数据共享 Manager
进程池 -可以获取返回值
同步调用 - 基本不用的
异步调用 - 重要的
apply_async
get获取结果
close
join
回调函数 Pool 回调函数在主进程中执行
apply_async(func = wahaha,callback = back)
posted @ 2018-07-27 08:16  九月江  阅读(399)  评论(0编辑  收藏  举报