• python之路-day32-管道、数据共享、进程池


    一、管道(不推荐使用,了解即可)

      进程间通信(IPC)方式二:管道(不推荐使用,了解即可),会导致数据不安全的情况出现,后面还会提到为什么

    会带来数据不安全的问题。

     1 #创建管道的类:
     2 Pipe([duplex]):在进程之间创建一条管道,并返回元组(conn1,conn2),其中conn1,conn2表示管道两端的连接对象,强调一点:必须在产生Process对象之前产生管道
     3 #参数介绍:
     4 dumplex:默认管道是全双工的,如果将duplex射成False,conn1只能用于接收,conn2只能用于发送。
     5 #主要方法:
     6     conn1.recv():接收conn2.send(obj)发送的对象。如果没有消息可接收,recv方法会一直阻塞。如果连接的另外一端已经关闭,那么recv方法会抛出EOFError。
     7     conn1.send(obj):通过连接发送对象。obj是与序列化兼容的任意对象
     8  #其他方法:
     9 conn1.close():关闭连接。如果conn1被垃圾回收,将自动调用此方法
    10 conn1.fileno():返回连接使用的整数文件描述符
    11 conn1.poll([timeout]):如果连接上的数据可用,返回True。timeout指定等待的最长时限。如果省略此参数,方法将立即返回结果。如果将timeout射成None,操作将无限期地等待数据到达。
    12  
    13 conn1.recv_bytes([maxlength]):接收c.send_bytes()方法发送的一条完整的字节消息。maxlength指定要接收的最大字节数。如果进入的消息,超过了这个最大值,将引发IOError异常,并且在连接上无法进行进一步读取。如果连接的另外一端已经关闭,再也不存在任何数据,将引发EOFError异常。
    14 conn.send_bytes(buffer [, offset [, size]]):通过连接发送字节数据缓冲区,buffer是支持缓冲区接口的任意对象,offset是缓冲区中的字节偏移量,而size是要发送字节数。结果数据以单条消息的形式发出,然后调用c.recv_bytes()函数进行接收    
    15  
    16 conn1.recv_bytes_into(buffer [, offset]):接收一条完整的字节消息,并把它保存在buffer对象中,该对象支持可写入的缓冲区接口(即bytearray对象或类似的对象)。offset指定缓冲区中放置消息处的字节位移。返回值是收到的字节数。如果消息长度大于可用的缓冲区空间,将引发BufferTooShort异常。
    管道介绍
     1 from multiprocessing import Process, Pipe
     2 
     3 def f(conn):
     4     conn.send("Hello 妹妹") #子进程发送了消息
     5     conn.close()
     6 
     7 if __name__ == '__main__':
     8     parent_conn, child_conn = Pipe() #建立管道,拿到管道的两端,双工通信方式,两端都可以收发消息
     9     p = Process(target=f, args=(child_conn,)) #将管道的一段给子进程
    10     p.start() #开启子进程
    11     print(parent_conn.recv()) #主进程接受了消息
    12     p.join()
    管道初使用
    1 应该特别注意管道端点的正确管理问题。如果是生产者或消费者中都没有使用管道的某个端点,就应将它关闭。这也说明了为何在生产者中关闭了管道的输出端,在消费者中关闭管道的输入端。如果忘记执行这些步骤,程序可能在消费者中的recv()操作上挂起(就是阻塞)。管道是由操作系统进行引用计数的,必须在所有进程中关闭管道的相同一端就会能生成EOFError异常。因此,在生产者中关闭管道不会有任何效果,除非消费者也关闭了相同的管道端点
    管道使用注意须知
    from multiprocessing import Process, Pipe
    
    def f(parent_conn,child_conn):
        #parent_conn.close() #不写close将不会引发EOFError
        while True:
            try:
                print(child_conn.recv())
            except EOFError:
                child_conn.close()
                break
    
    if __name__ == '__main__':
        parent_conn, child_conn = Pipe()
        p = Process(target=f, args=(parent_conn,child_conn,))
        p.start()
        child_conn.close()
        parent_conn.send('hello')
        parent_conn.close()
        p.join()            
    引发EOFError

    主进程将管道的两端都传送给子进程,子进程和主进程共用管道的两种报错情况,都是在recv接收的时候报错的:

        1.主进程和子进程中的管道的相同一端都关闭了,出现EOFError;

        2.如果你管道的一端在主进程和子进程中都关闭了,但是你还用这个关闭的一端去接收消息,那么就会出现OSError;

    二、数据共享(了解即可)

      基于消息传递的并发编程是大势所趋

      即便是使用线程,推荐做法也是将程序设计为大量独立的线程集合

      通过消息队列交换数据,这样极大的减少了对使用锁定和其他同步手段的需求,还可以扩展到分布式系统中

    注意:进程之间应当是尽量避免通信,即使需要通信,也应该选择进程安全的工具来避免加锁带来的问题。应该尽量避免使用

    共享数据的方法,以后会尝试使用数据库来解决进程之间的数据共享问题

      进程之间数据共享模块之一Manager模块:

    1 进程间数据是独立的,可以借助于队列或管道实现通信,二者都是基于消息传递的
    2 虽然进程间数据独立,但可以通过Manager实现数据共享,事实上Manager的功能远不止于此
    3 
    4 A manager object returned by Manager() controls a server process which holds Python objects and allows other processes to manipulate them using proxies.
    5 
    6 A manager returned by Manager() will support types list, dict, Namespace, Lock, RLock, Semaphore, BoundedSemaphore, Condition, Event, Barrier, Queue, Value and Array.
    Manager模块介绍

      多进程共同去处理共享数据的时候,就和我们多进程同时去操作一个文件中的数据一样,不加锁就会出现错误的结果,进程

    不安全的,所以也需要加锁

     1 from multiprocessing import Manager,Process,Lock
     2 def work(d,lock):
     3     with lock: #不加锁而操作共享的数据,肯定会出现数据错乱
     4         d['count']-=1
     5 
     6 if __name__ == '__main__':
     7     lock=Lock()
     8     with Manager() as m:
     9         dic=m.dict({'count':100})
    10         p_l=[]
    11         for i in range(100):
    12             p=Process(target=work,args=(dic,lock))
    13             p_l.append(p)
    14             p.start()
    15         for p in p_l:
    16             p.join()
    17         print(dic)
    Manager模块使用

      总结下,进程之间的通信:队列、管道、数据共享

    三、进程池

      multiprocess.pool 模块

      创建进程池的类:如果指定numprocess为3,则进程池会从无到有创建三个进程,然后自始至终使用这三个

    进程去执行使用所有的任务(高级一些的进程池可以根据你的并发量,搞成动态增加或减少进程池中进程数量的操作),

    不会开启其他进程,提高操作系统效率,减少空间的占用等

     1 numprocess:要创建的进程数,如果省略,将默认使用cpu_count()的值
     2 initializer:是每个工作进程启动时要执行的可调用对象,默认为None
     3 initargs:是要传给initializer的参数组
     4 
     5 p.apply(func [, args [, kwargs]]):在一个池工作进程中执行func(*args,**kwargs),然后返回结果。
     6 '''需要强调的是:此操作并不会在所有池工作进程中并执行func函数。如果要通过不同参数并发地执行func函数,必须从不同线程调用p.apply()函数或者使用p.apply_async()'''
     7 
     8 p.apply_async(func [, args [, kwargs]]):在一个池工作进程中执行func(*args,**kwargs),然后返回结果。
     9 '''此方法的结果是AsyncResult类的实例,callback是可调用对象,接收输入参数。当func的结果变为可用时,将理解传递给callback。callback禁止执行任何阻塞操作,否则将接收其他异步操作中的结果。'''
    10     
    11 p.close():关闭进程池,防止进一步操作。如果所有操作持续挂起,它们将在工作进程终止前完成
    12 
    13 P.jion():等待所有工作进程退出。此方法只能在close()或teminate()之后调用
    14 
    15 方法apply_async()和map_async()的返回值是AsyncResul的实例obj。实例具有以下方法
    16 obj.get():返回结果,如果有必要则等待结果到达。timeout是可选的。如果在指定时间内还没有到达,将引发一场。如果远程操作中引发了异常,它将在调用此方法时再次被引发。
    17 obj.ready():如果调用完成,返回True
    18 obj.successful():如果调用完成且没有引发异常,返回True,如果在结果就绪之前调用此方法,引发异常
    19 obj.wait([timeout]):等待结果变为可用。
    20 obj.terminate():立即终止所有工作进程,同时不执行任何清理或结束任何挂起工作。如果p被垃圾回收,将自动调用此函数
    Manager的方法
     1 import time
     2 from multiprocessing import Pool,Process
     3 
     4 #针对range(100)这种参数的
     5 # def func(n):
     6 #     for i in range(3):
     7 #         print(n + 1)
     8 
     9 def func(n):
    10     print(n)
    11     # 结果:
    12     #     (1, 2)
    13     #     alex
    14 def func2(n):
    15     for i in range(3):
    16         print(n - 1)
    17 if __name__ == '__main__':
    18     #1.进程池的模式
    19     s1 = time.time()  #我们计算一下开多进程和进程池的执行效率
    20     poll = Pool(5) #创建含有5个进程的进程池
    21     # poll.map(func,range(100)) #异步调用进程,开启100个任务,map自带join的功能
    22     poll.map(func,[(1,2),'alex']) #异步调用进程,开启100个任务,map自带join的功能
    23     # poll.map(func2,range(100))  #如果想让进程池完成不同的任务,可以直接这样搞
    24     #map只限于接收一个可迭代的数据类型参数,列表啊,元祖啊等等,如果想做其他的参数之类的操作,需要用后面我们要学的方法。
    25     # t1 = time.time() - s1
    26     #
    27     # #2.多进程的模式
    28     # s2 = time.time()
    29     # p_list = []
    30     # for i in range(100):
    31     #     p = Process(target=func,args=(i,))
    32     #     p_list.append(p)
    33     #     p.start()
    34     # [pp.join() for pp in p_list]
    35     # t2 = time.time() - s2
    36     #
    37     # print('t1>>',t1) #结果:0.5146853923797607s 进程池的效率高
    38     # print('t2>>',t2) #结果:12.092015027999878s
    进程池的简单应用及与进程池的效率对比
  • 相关阅读:
    [Linux]调整swap
    [Linux]mysql错误总结-ERROR 1067 (42000): Invalid default value for TIMESTAMP
    Canvas动画:地球绕着太阳转
    50个好用的前端框架,建议收藏!
    flex布局属性说明
    纯CSS绘制的图形一览
    深入理解CSS盒模型(转)
    JS的防抖与节流学习笔记
    应用八:Vue之在nginx下的部署实践
    css元素居中的几种方式
  • 原文地址:https://www.cnblogs.com/alvin-jie/p/10041017.html
Copyright © 2020-2023  润新知