• python进阶(五、并发编程:池、协程)


    2.并发编程
    2.6 池:concurrent.futures模块
    2.6.1 简介
    池:预先的开启固定个数的进程数/线程数,当任务来临的时候,直接提交给已经开好的进程/线程,让这个进程/线程去执行就可以了。
    池节省了进程、线程的开启、关闭、切换需要的时间,并且减轻了操作系统调度的负担。
    当有新的请求提交到进程池中时,如果池未满,则会分配一个进程用来执行该请求;反之,如果池中的进程数已经达到规定最大值,那么该请求就会等待,只要池中有进程空闲下来,该请求就能得到执行。
    池缺点:一个池中的任务个数限制了我们程序的并发个数
    concurrent.futures模块提供了高度封装的异步调用接口
    ThreadPoolExecutor:线程池,提供异步调用
    ProcessPoolExecutor: 进程池,提供异步调用
    concurrent.futures模块出现过程:
    (1)threading模块没有提供池
    (2)multiprocessing模块仿照threading编写,并增加了Pool功能
    (3)concurrent.futures模块出现,包含了线程池和进程池,两者使用方法相似
    通常进程池中进程的数量= CPU核数+1(一般大于CPU核心数,小于CPU核数2)
    进程池适用于高计算场景(没有I/O操作:没有文件操作,没有数据库操作、没有网络操作、没有input),这种场景很少。
    通常线程池中线程的数量= CPU核数
    5(一般根据I/O比例定制,推荐CPU核数*5)
    线程池和进程池的用法基本相同,下面代码会用线程池或进程池编写

    2.6.2 submit()启用线程(进程)池:
    (1)创建线程池:实例化
    (2)将任务放入线程
    使用线程池,打印当前线程id:

    增加时延占用线程

    传参:

    启用进程池:进程池的用法和线程池用法基本相同

    2.6.3 result()获取任务结果
    使用ret.result()获取返回结果,程序会同步阻塞,不再并发运行

    使用列表接收返回对象,最后统一打印。程序恢复并发

    也可以使用字典存储返回对象

    2.6.4 map()取代for循环和submit的操作

    传多个参数

    可迭代对象使用列表

    线程map

    2.6.5 add_done_callback()回调函数
    谁先执行完,先处理谁。处理速度最快,效率最高。但数据的顺序是混乱的

    想要识别任务和结果的对应关系,可以给结果增加标识组成元组

    实例:分析网页信息


    执行结果:

    2.7 协程
    2.7.1 协程概念(重要)
    对于单线程下,我们不可避免程序中出现io操作,但如果我们能在自己的程序中(即用户程序级别,而非操作系统级别)控制单线程下的多个任务能在一个任务遇到io阻塞时就切换到另外一个任务去计算,这样就保证了该线程能够最大限度地处于就绪态,即随时都可以被cpu执行的状态,相当于我们在用户程序级别将自己的io操作最大限度地隐藏起来,从而可以迷惑操作系统,让其看到:该线程好像是一直在计算,io比较少,从而更多的将cpu的执行权限分配给我们的线程。
    1)协程是操作系统不可见的
    协程的本质就是在单线程下,由用户自己控制一个任务遇到io阻塞了就切换另外一个任务去执行,以此来提升效率。
    协程的所有切换都基于用户,只有在用户级别能够感知到的IO才能用协程模块做切换来规避(例如:socket、请求网页的操作)。
    一些和文件相关的IO操作,只有操作系统能够感知到,只能使用线程来规避

    2)协程的优点:
    减轻了操作系统的负担
    造成线程很忙的现象,可以得到更多的cpu的执行权限(时间片)

    3)进程、线程、协程的区别:
    进程:数据隔离 数据不安全 操作系统级别 开销非常大 可以利用多核
    线程:数据共享 数据不安全 操作系统级别 开销非常小 不能利用多核 对IO更敏感
    协程:数据共享 数据安全(单线程) 用户级别 开销最小 不能利用多核 不能识别文件操作IO

    4)实现协程的2个模块:
    gevent模块和asyncio模块

    2.7.2 gevent第三方模块(重要)
    Gevent是一个第三方库。
    gevent模块利用了greenlet底层模块来完成切换,加上自己的规避I/O功能
    1)协程实现

    主程序中添加IO,可以调用协程。但主程序IO中断时间较短,协程程序无法运行完成

    增加IO时长或增加IO次数,可以使协程完成运行。

    推荐:创建阻塞,直到协程执行完成

    2)协程并发

    阻塞多个指定的协程

    2.7.3 猴子补丁
    使用time.sleep()会影响协程的并发,同样socket、requests等模块执行时,都会遇到该问题。

    1)解决time、socket、requests等模块不能并发问题:
    在time模块之前导入monkey,并执行“monkey.patch_all()”

    2)判断猴子补丁是否有效

    源码中值为True的默认支持,没有列出来的不支持

    总结:将IO操作写到函数里,然后将函数提交gevent。在需要使用函数结果的地方使用join()制造阻塞。

    3)基于gevent协程和线程池,实现socket并发

    4)4核cpu能处理的并发数:
    进程数:5
    线程:20
    协程:500
    最大并发数 = 5 * 20 * 500 = 5W,已经足够使用。

    2.7.4asyncio 内置模块
    asyncio模块是python原生的内置模块
    asyncio模块利用了yield底层模块来完成切换,加上自己的规避I/O功能
    两个关键字:
    async(重要)
    await(重要)
    1)功能实现

    2)实现并发

    3)asyncio使用协程完成http访问

  • 相关阅读:
    Stage划分原理
    Spark转换和动作算子
    Spark运行原理
    Scrapy数据持久化
    在实际工作中Eclipse常用的快捷键
    关于Linux(CentOS6.5)中文输入法、中文桌面可视化等问题
    Eclipse无法启动报An internal error occurred during: "reload maven project". java.lang.NullPointerExceptio错原因及解析
    在Eclipse中复制导入项目并且修改原来的项目名字,项目后面的括号显示原来项目的名字!
    response.sendRedirect()与request.getRequestDispatcher("/index.jsp").forward(request, response)两者辨析
    MyEclipse/Eclipse导出jar方法
  • 原文地址:https://www.cnblogs.com/bdzxh/p/14081330.html
Copyright © 2020-2023  润新知