队列,管道,manager模块

###生产者消费者关系###

  • 主要是解耦(高内聚,低耦合),借助队列来实现生产者消费者 模型
  • 栈:先进后出(First In Last Out 简称:FILO)
  • 队列:先进先出(First In First Out 简称:FIFO)
  • import queue  .............不能进行多进程之间的数据传输

1,队列:from multiprocessing import Queue借助Queue来解决生产者消费者模型,队列是安全的

  • 1.1>q = Queue(num)也只能是数据一次次的放,一次次的拿(一进一出)
    • num:队列的最大长度(表示对列里能刚的最多数据,当数据量较大时就会阻塞,等待进入到队列中)
    • q .get():表示获取队列中的数据,如果队列中有数据是直接获取,此时就没有阻塞等待这么一说了,当队列没有数据时,它就会在这阻塞等获取数据.
    • q.put():表示向管道里发送数据,当管道数据未满,(管道里被获取数据的速度大于发送的速度时,)这时就可以直接放到队列里,如果管道数据满(管道获里被获取数据的速度小于发送的速度时)发送端(生成者)这时就会处于阻塞状态
    • q.get_nowait():不阻塞:队列里有数据时就直接获取,没有时就会直接报错(比较敏感,不能容忍)
    • q.put_nowait():,不阻塞:,如果可以继续往队列中放数据,就直接放,不能就会报错(比较敏感,不能容忍)
  • 1.2>JoinableQueue###可连接的队列
    • from multiprocessing import JoinableQueue是继承了Queue,所以可以使用Queue中的方法
    • 并且JoinableQueue又多了两个方法
    • q.join():用于生产者.表示等待消费者q.task_done返回一个标识(ack),生产者就能获得消费者当前消费了多少个数据
    • q.task_done()#用于消费者,是指每个消费队列中的一个数据,就给join返回一个标识

###第一种方法###是在生产者q.put(None)来给消费者提示队列的数据被消费完.(是子进程与子进程之间的通信)

from multiprocessing import Process,Queue  # 导入队列的模块Queue
def consumer(q):  # .定义消费者函数来生成在消费者的子进程
    num = q.get()  # .通过q.get()来获取队列里的数据
    if num == None  # 判断当num的数据是None的时候就执行下边的代码
        print("队列空了...")
    else:  # 当条件不成立的时候就打印这个数
        print(num)
def producer(q):  # 定义来生成生产者的子进程
    for i in range(10):  # 连续生产10个数
        num = i+1
        q.put(num)  # 把生产出来的数放到队列里
        q.put(None)  # 当数据生产完毕,就在队列里放一个None
if __name__ == "__main__":
    q = Queue()  # .实例化一个队列的对象,括号里可以放数,表示队列可以放多少数据
    con = Process(target=consumer,args=(q,))
    pro = Process(target=producer,args=(q,))
    con.start()
    pro.start()

###第二种方法###在主程序q.put(None)来控制消费者是否全部去到了生产者的数据(主进程和子进程之间的通信)

from multiprocessing import Queue,Process
import time

def consumer(q,name,color):
    while 1:
        info = q.get()
        if info:
            print('%s %s 拿走了%s 33[0m'%(color,name,info))
        else:# 当消费者获得队列中数据时,如果获得的是None,就是获得到了生产者不再生产数据的标识
            break# 此时消费者结束即可

# 消费者如何判断,生产者是没来得及生产数据,还是生产者不再生产数据了?
# 如果你尝试用get_nowait() + try 的方式去尝试获得生产者不再生产数据,此时是有问题的。

def producer(q,product):
    for i in range(20):
        info = product + '的娃娃%s号'%str(i)
        q.put(info)

if __name__ == '__main__':
    q = Queue(10)
    p_pro1 = Process(target=producer,args=(q,'岛国米饭保你爱'))
    p_pro2 = Process(target=producer,args=(q,'苍老师版'))
    p_pro3 = Process(target=producer,args=(q,'波多多版'))
    p_con1 = Process(target=consumer,args=(q,'alex','33[31m'))
    p_con2 = Process(target=consumer,args=(q,'wusir','33[32m'))
    p_l = [p_con1,p_con2,p_pro1,p_pro2,p_pro3]
    [i.start() for i in p_l]
    # 父进程如何感知到生产者子进程不再生产数据了?
    p_pro1.join()
    p_pro2.join()
    p_pro3.join()
    q.put(None)# 几个消费者就要接受几个结束标识
    q.put(None)

###第三种方法###是通过JoinableQueue来实现子进程和子进程和主进程之间相互通信

from multiprocessing import JoinableQueue,Process  # 导入一个新模块JoinableQueue
def consumer(q):
    num = q.get()
    print(num)
    q.task_done()  # task_done是没get()到一个值就会返回给join(生产者)一个标识
def producer(q):
    for i in range(10):
        num = i +1
        q.put(num)
        q.join()  # 等待全部接受完task_done生产者才完完全全结束(此时是阻塞等待)
if __name__ == "__main__":
    q = JoinableQueue()
    con = Process(target=consumer,args=(q,))
    pro = Process(target=producer,args=(q,))
    con.daemon = True
    con.start()
    pro.start()
    pro.join()
  • 等到生产者的代码执行完毕,再开始执行主程序的代码,当主程序执行完毕就会把设置成守护进程停止,这时会强制关停守护进程的的while循环.

###主程序等待生产者程序只想完毕再执行,生产者程序会等待消费者程序执行完毕才执行完毕,这时主城序的执行完毕,又会将消费者程序强制停止,形成一个关系作用环

2,管道 

  2.1>单进程下的管道

from multiprocessing import Pipe
con1,con2 = Pipe()
con1.send("liangxue")
print(con2.recv())  # .此时打印的是abc,con1发送的只能是con2接收
con2.send([1,2,3])
print(con1.recv())  # 此时打印的是[1,2,3]con2发送只能是con1发送

   

###不能con1发送,con1接收时不会包错,发送端不能做为接收端### 

  • 2.2>多进程下的管道(管道都有2端,一端接收,一端发送)
from multiprocessing import Process,Pipe  # 导入Process模块和Pipe模块
def func(con1,con2):  # 定义一个创建子进程的函数
    con1.close()  # .如果用con2发送,就把con1关掉,要不然会一直处于阻塞状态
    while 1:
        try:  # 是用抛异常机制来从管道取东西
            print(con2.recv())
        except EOFError:
            con2.close()  # 取完数据数据以后就把con2关掉 ,要不然会一直处于阻塞状态
            break  # 跳出循环,要不然会一直死循环

if __name__ == "__main__":
    con1,con2 = Pipe()  # 实例化的管道得到的是2个对象(管道的特性)
    p = Process(target=func,args=(con1,con2))
    p.start()
    con2.close()  # 这个其实不用去关,不会处于阻塞状态,但为了代码的严谨,要关掉
    for i in range(10):
        con1.send(i)
        con1.close()  # 这个必须关掉,要不接受端一直处于阻塞状态.

3,多进程之间的内存资源共享

from multiprocessing import Process, Manager  # 导入Manager模块
def func():  # .定义一个创建 子程序的函数
    num[0] -= 1  # 把可迭代对象(列表)的第0项数字减一
    print("子进程中的num的值是",num)
if __name__ == "__main__":
    m = Manager()  # 实例化Manager这个对象
    num = m.list([1,2,3])  # 共有资源列表,固定写法:m.数据类型()
    p = Process(target=func,args=(num,))
    p.start()
    p.join()  # 等子进程把共有的资源修改完再执行下一步操作,要不然会报错,数据容易混乱
    print("父进程中的num的值是",num)

4,进程池:一个存放有一定数量进程的池子,这些进程一直处于待命状态,一旦有任务来,马上就有进程去处理.因为在实际业务中,任务量是有多有少的,如果任务量特别的多,不可能要开对应的进程,第一,开启进程需要大量 的时间让操作系统来为你管理它,其次还需要消耗大量时间让cpu帮你调度

进程池的优点:会帮助程序员去管理进程池中的进程池

进程池的最佳开启数量:核数+1,核数可以由os模块去get到,进程池的进程均为子进程,不用.start(),进程池会帮你开启进程池

  • from multiprocessing import pool
  • p = Pool(os.cpu_count() + 1)
  • 4.1>map方法:map(function,iterable)
    • function:进程池中的进程执行的任务函数
    • iterable:可迭代对象,是把可迭代对象中的每个元素依次传给任务函数当参数
from multiprocessing import Pool  # 导入Pool这个数据池
def func(num):
    num = num + 1  # 把map中可迭代对象的每一个值传过来做处理
    print(num)
    return num  # 此时return的每一个值(经过处理后)都会在此放到列表中

if __name__ == "__main__":
    p = Pool(5)  # 实例化5个进程的数据池
    ret = p.map(func,[i for i in range(10)]).这的可迭代对象会把每一个数据作为参数传给函数
    p.close()  # 关闭进程池的大门,不允许其他任务再进入到进程池,防止进程处理混乱
    p.join()  # 等待进程池中的数据处理完毕再执行主进程代码
    print("主进程中的map的返回值是",ret)

  • 4.2>apply(function,args=()):表示同步的效率,也就是说进程池中的内容一个一个的去执行
    • function:进程池中的进程执行的任务函数,args:可迭代对象的参数,是传给任务函数的参数
    • 同步处理任务时,不需要close()和join
  • 同步处理任务时,进程池中的所有进程是普通进程(主进程需要等待其执行结束)
  • appy_async(func,args=(),callback=None):表示异步的效率,也就是说进程池中的进程一次性都去执行任务
    • func:进程池中的进程执行的任务函数
    • args:可迭代对象的参数,是传给任务函数的参数
    • callback:回调函数,就是说没当进程池中有进程处理完了,返回的结果可以交给回调函数,由回调函数进行进一步的处理,回调函数只有异步有,同步是没有的
  • 异步处理任务时,进程池中的所有进程是守护进程(主进程代码执行完毕守护进程就结束)
  • 异步处理任务时,必须加上close和join
  • 回调函数的使用:
    • 进程的任务函数的返回值,被当成回电函数的形参接收到,以此进行进一步的处理操作
    • 回调函数是由主进程调用到的,而不是子进程,子进程值负责把结果传递给回调函数
  • 4.2.1>apply同步效率(结果是一个一个出的,因此效率比较低下,比较耗时)
from multiprocessing import Pool
import time
def func(num):
    time.sleep(0.5)
    num += 1
    print(num)
if __name__ == "__main":
    p = Pool(5)  # 实例化一个5个进程的进程池(这5个进程池都是普通子进程)
    for i in range(10):
        p.apply(func,args=(i,))  # p.apply(上边的执行函数,是可迭代对象的参数)
        p.close()
        p.join()
  • 当加上p.close()时,在p.join()上边还好,不会报错,因为关闭了进程池的大门,所以join与不join每没有太大区别,当p.close()在p.join()的下一道程序,就会报错(程序执行到最后一个值的时候)报一个断言的错误,按时代码会执行完 
  • 4.2.2>异步的效率(异步是因为同时进行进程中的几条进程,所以耗时少,效率较高)
from multiprocessing import Pool
import time

def func(num):
    time.sleep(0.5)
    num += 1
    print(num)
if __name__ == "__main__":
    p = Pool(5)  # 实例化一个放5个进程的进程池(实守护进程例化的进程是)
    for i in range(10):
        p.apply_async(func,args=(i,))  # p.apply_async(上边执行的函数,可迭代对象的参数)
        p.close()  # 这必须有了,对于进程池的进程和数据比较安全
        p.join()  # join主,因为开启的子进程都是守护进程,不join主,守护进程就不会执行了

这时的close和join就必须得加上了, 且顺序不能乱,,要么包一个断言的错误,要么就是代码执行完,每有打印任何结果,

  • 2.2.3>回调函数(进程的任务函数返回值,被当做是参数返回给回调函数,当做回调函数的参数)来进一步处理
from multiprocessing import Pool  # 导入Pool进程池
def func(num):  # 定义一个进程的任务函数
    num += 1  # .操作进程处理来的数据
    return num  # 此时返回给回调函数,并做为回调函数的参数
def cal_back(num):  # 定义回调函数
    num = num + 10  # 操作进程任务函数返回来的参数
    print(num)
if __name__ == "__main__":
    p = Pool(5)  # .实例化一个存放有5个进程的进程池
    for i in range(10):  # 传原始数据
        p.apply_async(func,args=(i,),callback=cal_back)  # 三个参数:进程的任务函数,穿的原始参数,                                                                                                                    回调函数
        p.close()
        p.join()
  • 进程池异步处理(一次性处理进程池里的相应数量的进程)时间利用率较高,而同步处理即使进程池里有一定数量的进程,也是一条一条的去执行,因此时间利用率相对较低
原文地址:https://www.cnblogs.com/ljc-0923/p/9632568.html