• 管道 数据共享


    管道 (不常用) 数据不安全 (会有多个端口争抢同一条数据) 所以要加锁    全双工通道

      应注意 :如果是生产者和消费者都没有使用管道的某个端点 就应该关闭 如果不关闭 程序可能在消费者的recv()操作上挂起(阻塞). E

    OFError出现的两种情况  1. 主进程和子进程中都关闭了  2. 管道的一端在主进程和子进程中都关闭了 但是还用这个关闭的一段去接收消息 就会出现OSError:

      所以关闭管道的时候 就容易出现问题 需要将所有只用这个管道的进程中两端全部关闭 也可以通过异常捕获来处理

      关于管道会造成数据不安全问题的官方解释 :  

        The two connection objects returned by Pipe() represent the two ends of the
    pipe. Each connection object has send() and recv() methods (among others). Note that
    data in a pipe may become corrupted if two processes (or threads) try to read from
    or write to the same end of the pipe at the same time. Of course there is no risk of
    corruption from processes using different ends of the pipe at the same time.
      由Pipe方法返回的两个连接对象表示管道的两端。每个连接对象都有send和recv方法(除其他之外)。注
    意,如果两个进程(或线程)试图同时从管道的同一端读取或写入数据,那么管道中的数据可能会损坏。当然,在
    使用管道的不同端部的过程中不存在损坏风险

      数据共享 : 主进程数据可以让子进程进行修改 Manager模块
    from multiprocessing import Process,Manager,Lock
    def func(m_dic,ml):
        with ml:  # 如果不加锁 肯定会出现数据错乱
            m_dic['count'] -= 1
    #  模拟数据共享不安全
    if __name__ == '__main__':
        m = Manager()
        ml = Lock()
        m_dic = m.dict({"count":100})
        print('主进程',m_dic)
        p_lst = []
        for i in range(20):
            p1 = Process(target=func,args=(m_dic,ml,))
            p1.start()
            p_lst.append(p1)
        [p.join() for p in p_lst]
        print('主进程', m_dic)

        进程之间的通信 : 队列 管道 数据共享也算

        加锁的第二种写法 with a:  (a是lock实例化的一个对象) 类似于打开文件的两种方式 

      进程池   避免系统反复的进程的创建以及销毁的过程 从而 提高效率  所以将进程放到一个池子里 用的时候从池子里调用 用完再放回去 避免了上述过程  也就是说 池中进程的数量是一定的,那么同一时间最多有固定数量的进程在运行 这样不会增加操作系统的调度难度 还节省了开闭进程的时间 也一定程度上能够实现并发效果.

        创建进程池的类 : 如果指定multiprocess 为3,则进程池会创建三个进程, 然后自始至终使用这三个进程去执行所有的任务 不会开启其他进程 提高操作系统效率,减少空间占用等.

    import time
    from multiprocessing import Process,Pool
    def func(n):
        for i in range(5):
            n = n + i
        print(n)
    if __name__ == '__main__':
        pool_start_time = time.time()
        pool = Pool(4)  # 数据池设定4个进程
        pool.map(func,range(100))
        pool_end_time = time.time()
        pool_dif_time = pool_end_time - pool_start_time
        p_s_time = time.time()
        p_lst = []
        for i in range(100):
            p1 = Process(target=func,args=(i,))
            p1.start()
            p_lst.append(p1)
        [p.join() for p in p_lst]
        p_e_time = time.time()
        p_dif_time = p_e_time - p_s_time
        print('进程池的执行时间', pool_dif_time)  # 进程池的执行时间 0.20521235466003418
        print('多进程的执行时间', p_dif_time)   # 多进程的执行时间 4.232760906219482
    多进程和进程池执行时间的比较

        从图中可看出 进程池的效率远高于多进程  有一点 map是异步执行的 并且自带close 和join.   一般约定俗成的是进程数量为cpu的数量 .

        同步和异步的两种执行方式:

    # #进程池的同步执行方法
    from multiprocessing import Pool
    import time
    def func(i):
        time.sleep(1)
        return i ** 2
    if __name__ == '__main__':
        p = Pool(2)  #进程池中从无到有创建四个进程 以后一直是这三个进程在执行任务
        res_l = []
        for i in range(5):
            res = p.apply(func, args=(i,))  # 同步调用 直到本次任务执行完毕拿到res 等待任务work执行的过程中可能阻塞也可能不阻塞
            res_l.append(res)
        print(res_l)   # [0,1,4,9,16,25]   #可以直接拿到返回值

        异步执行方式

    # 进程池的异步执行方法
    import time
    from multiprocessing import Pool
    def func(i):
        time.sleep(1)
        return i ** 2
    if __name__ == '__main__':
        p = Pool(4)  #从无到有创建四个进程 以后一直是这四个进程在执行任务
        res_lst = []
        for i in range(10):
            res = p.apply_async(func, args=(i,))  # 异步运行 ,根据进程池中有的进程数,每次最多3个子进程在异步执行,并且可以执行不同的
                                                  # 任务,传送任意的参数了。
                                                  # 返回结果之后,将结果放入列表,归还进程,之后再执行新的任务
                                                  # 需要注意的是,进程池中的三个进程不会同时开启或者同时结束
                                                  # 而是执行完一个就释放一个进程,这个进程就去接收新的任务。
            res_lst.append(res)
            # 异步apply_async用法: 如果异步提交的任务 主进程需要使用join 等待进程池内
            # 任务都处理完,然后可以用get收集结果   否则 进程池可能还没来得及执行 就跟着一起结束了
            # p.close()  # 不是关闭进程池 而是结束进程池接收任务 确保没有新任务再提交过来
            # p.join()  # 感知进程的任务已经执行结束,只有当没有新的任务添加进来的时候 才能感知到任务结束了 所以在join之前必须加上close方法
        for res in res_lst:
            print(res.get())  # 使用get来获取apply_aync的结果 如果是apply 则没有get方法 , 因为apply是同步执行,立刻获取结果 无序get
                # 结果就是 每次产生四个数据 直到进程完成执行

      回调函数 :  

    需要回调函数的场景:进程池中任何一个任务一旦处理完了,就立即告知主进程:我好了额,你可以处理我的结果
    了。主进程则调用一个函数去处理该结果,该函数即回调函数,这是进程池特有的,普通进程没有这个机制,但是
    我们也可以通过进程通信来拿到返回值,进程池的这个回调也是进程通信的机制完成的。 我们可以把耗时间(阻塞)的任务放到进程池中,然后指定回调函数(主进程负责执行),这样主进程在执行回调
    函数时就省去了I/O的过程,直接拿到的是任务的结果
    import os
    from multiprocessing import Pool
    
    def func1(n):
        print('func1>>',os.getpid())
        # print('func1')
        return n*n
    
    
    def func2(nn):
        print('func2>>',os.getpid())
        # print('func2')
        print(nn)
        # import time
        # time.sleep(0.5)
    if __name__ == '__main__':
        print('主进程:',os.getpid())
        p = Pool(4)
        p.apply_async(func1,args=(10,),callback=func2)
        p.close()
        p.join()

        回调函数在写的时候注意一点,回调函数的形参只有一个,如果你的执行函数有多个返回值,那么也可以被回调函数的这一个形参接收,接收的是一个元组,包含着你执行函数的所有返回值。

        

  • 相关阅读:
    Epox 8RDA3G主板奇怪的问题
    Dreamweaver自动生成的垃圾代码
    于今天完成NGW作业
    C#中WebBrowser的使用
    解决FC3下默认浏览器无法出来的问题
    开了几个小时的会……
    HappyEO电子琴
    又一个周末
    FC2/FC3下无法使用Midi设备
    Blog正式易名“小生杂谈”
  • 原文地址:https://www.cnblogs.com/f-g-f/p/9851562.html
Copyright © 2020-2023  润新知