• python并发编程之多进程2数据共享及进程池和回调函数


    一、数据共享

    尽量避免共享数据的方式

    可以借助队列或管道实现通信二者都是基于消息传递的。

    虽然进程间数据独立,但可以用过Manager实现数据共享,事实上Manager的功能远不止于此。

    命令就是一个程序,按回车就会执行(这个只是在windows情况下)
    tasklist 查看进程
    |就是管道(tasklist执行的内容就放到管道里面了,
    管道后面的findstr  pycharm就接收了)

    管道和队列

     Manager,Process,Lock
     work(dic,mutex):
     mutex.acquire()
     dic['count']-=1
     mutex.release()
     也可以这样加锁
        with mutex:
    '] -= 1
    :
     Lock()
    实现共享,由于字典是共享的字典,所以得加个锁
    })
     []
    ):
    (share_dic,mutex))
    先添加进去
            p.start()
     p_l:
            i.join()
    (share_dic)
     共享就意味着会有竞争,
    数据共享

    二、进程池

    在利用Python进行系统管理的时候,特别是同时操作多个文件目录,或者远程控制多台主机,并行操作可以节约大量的时间。多进程是实现并发的手段之一,需要注意的问题是:

    1. 很明显需要并发执行的任务通常要远大于核数
    2. 一个操作系统不可能无限开启进程,通常有几个核就开几个进程
    3. 进程开启过多,效率反而会下降(开启进程是需要占用系统资源的,而且开启多余核数目的进程也无法做到并行)

    例如当被操作对象数目不大时,可以直接利用multiprocessing中的Process动态成生多个进程,十几个还好,但如果是上百个,上千个。。。手动的去限制进程数量却又太过繁琐,此时可以发挥进程池的功效。

    那么什么是进程池呢?进程池就是控制进程数目

     ps:对于远程过程调用的高级应用程序而言,应该使用进程池,Pool可以提供指定数量的进程,供用户调用,当有新的请求提交到pool中时,如果池还没有满,那么就会创建一个新的进程用来执行该请求;但如果池中的进程数已经达到规定最大值,那么该请求就会等待,直到池中有进程结束,就重用进程池中的进程。 

    进程池的结构:

     创建进程池的类:如果指定numprocess为3,则进程池会从无到有创建三个进程,然后自始至终使用这三个进程去执行所有任务,不会开启其他进程

    1.创建进程池

    Pool([numprocess  [,initializer [, initargs]]]):创建进程池 

    2.参数介绍

     numprocess:要创建的进程数,如果省略,将默认为cpu_count()的值,可os.cpu_count()查看
     initializer:是每个工作进程启动时要执行的可调用对象,默认为None
     initargs:是要传给initializer的参数组

    3.方法介绍

    p.apply(func [, args [, kwargs]]):在一个池工作进程中执行
    func(*args,**kwargs),然后返回结果。
    需要强调的是:此操作并不会在所有池工作进程中并执行func函数。
    如果要通过不同参数并发地执行func函数,必须从不同线程调用p.apply()
    函数或者使用p.apply_async()
    
    
    p.apply_async(func [, args [, kwargs]]):在一个池工作进程中执行func(*args,**kwargs),然后返回结果。
    此方法的结果是AsyncResult类的实例, callback是可调用对象,接收输入参数。当func的结果变为可用时, 将理解传递给callback。callback禁止执行任何阻塞操作, 否则将接收其他异步操作中的结果。 p.close():关闭进程池,防止进一步操作。禁止往进程池内在添加任务(需要注意的是一定要写在close()的上方
    P.jion():等待所有工作进程退出。此方法只能在close()或teminate()之后调用

    应用1:

     Pool
     os,time
     task(n):
    os.getpid())
    )
    os.getpid())
    return n**2
    :
     print(os.cpu_count())  #查看cpu个数
    最大四个进程
    开7个任务
    同步的,等着一个运行完才执行另一个
    res)
    禁止往进程池内在添加任务
    在等进程池
    ')
    apply同步进程池(阻塞)(串行)
     ----------------
     那么我们为什么要用进程池呢?这是因为进程池使用来控制进程数目的,
     我们需要几个就开几个进程。如果不用进程池实现并发的话,会开很多的进程
     如果你开的进程特别多,那么你的机器就会很卡,所以我们把进程控制好,用几个就
     开几个,也不会太占用内存
     Pool
     os,time
     walk(n):
    os.getpid())
    )
    return n**2
    :
    )
     []
    ):
    (i,))
     print(res)  #打印出来的是对象
    那么现在拿到的是一个列表,怎么得到值呢?我们用个.get方法
    禁止往进程池里添加任务
         p.join()
     print(res_obj_l)
    这样就得到了
    apply_async异步进程池(非阻塞)(并行)

    那么什么是同步,什么是异步呢?

    同步就是指一个进程在执行某个请求的时候,若该请求需要一段时间才能返回信息,那么这个进程将会一直等待下去,直到收到返回信息才继续执行下去

    当有消息返回时系统会通知进程进行处理,这样可以提高执行的效率

    什么是串行,什么是并行呢?

    举例:能并排开几辆车的就可以说是“并行”,只能一辆一辆开的就属于“串行”了。很明显,并行的速度要比串行的快得多。(并行互不影响,串行的等着一个完了才能接着另一个)

    应用2:

    使用进程池维护固定数目的进程(以前客户端和服务端的改进)

    import *
     Pool
     socket(AF_INET,SOCK_STREAM)
    端口重用
    ))
    )
    )
     talk(coon,addr):
     通信循环
    :
    )
    ))
    break
                coon.send(cmd.upper())
    ))
     Exception:
    break
        coon.close()
    :
    )
    链接循环
     s.accept()
    (coon,addr)
    (coon,addr))
        s.close()
    因为是循环,所以就不用p.join了
    服务端
    import *
     socket(AF_INET,SOCK_STREAM)
    ))
     True:
    ).strip()
    continue
    ))
    )
    ))
    10 c.close()
    客户端

    三、回调函数

    回调函数什么时候用?(回调函数在爬虫中最常用)
    造数据的非常耗时
    处理数据的时候不耗时
    
    
    你下载的地址如果完成了,就自动提醒让主进程解析
    谁要是好了就通知解析函数去解析(回调函数的强大之处)

    需要回调函数的场景:进程池中任何一个任务一旦处理完了,就立即告知主进程:我好了额,你可以处理我的结果了。主进程则调用一个函数去处理该结果,该函数即回调函数

    我们可以把耗时间(阻塞)的任务放到进程池中,然后指定回调函数(主进程负责执行),这样主进程在执行回调函数时就省去了I/O的过程,直接拿到的是任务的结果。

     Pool
     requests
     os
     time
     get_page(url):
    (os.getpid(),url))
    得到地址
    )
    (os.getpid(),url))
    :response.text}
     parse_page(res):
    '''
    ]))
    ) as f:
    ]))
            f.write(parse_res)
    :
    )
     [
    ,
    ,
    ,
    ,
    '
        ]
     urls:
    parse_page)
        p.close()
        p.join()
    都不用.get()方法了
    回调函数(下载网页的小例子)

    如果在主进程中等待进程池中所有任务都执行完毕后,再统一处理结果,则无需回调函数

     Pool
     requests
     os
     get_page(url):
    (os.getpid(),url))
    得到地址  response响应
    :response.text}
    :
    )
     [
    ,
    ,
    ,
    ,
    '
        ]
     []
     urls:
    (url,))
            obj_l.append(obj)
        p.close()
        p.join()
    in obj_l])
    下载网页小例子(无需回调函数)
  • 相关阅读:
    iptables之NAT端口转发设置
    Windows server 2008 R2远程桌面3389端口号修改
    Nginx启动错误:error while loading shared libraries: libpcre.so.1
    sp_change_users_login 'Update_One', '用户名', '登录名';
    Sqlserver 数据库定时自动备份
    ES DSL 基础查询语法学习笔记
    kafka命令使用
    kafka集群中常见错误的解决方法:kafka.common.KafkaException: Should not set log end offset on partition
    快速排序法(双冒泡排序法)
    运算符
  • 原文地址:https://www.cnblogs.com/morgana/p/8491276.html
Copyright © 2020-2023  润新知