zoukankan      html  css  js  c++  java
  • python网络编程 day36 协程gevent asyncio模块

    一、内容回顾

    面试题:

    • 请聊聊进程队列的特点和实现原理

      • 进程之间可以互相通信 IPC

      • 数据安全

      • 先进先出

      • 实现原理

        • 管道+锁

        • 管道是基于文件级别的socket+pickle实现的

    • 你了解生产者消费者模型吗?

      • 了解

      • 为什么了解?

        • 工作经历:

        • 采集图片,爬取音乐,主要是爬取大量数据,想提高爬虫效率,有用过一个生产者消费者模型,这个模型使用的是消息中间件,用的是redis,获取网页的过程作为生产者,分析与获取歌曲作为消费者

      • 在python中实现生产者消费者模型可以用那些机制

        • 消息中间件

        • celery:定时发送短信的任务

    • 从你的角度说说进程在计算机中扮演什么角色

      • 资源分配的最小单位

      • 进程与进程之间内存隔离

      • 进程是由操作系统负责调度,并且进程与进程之间是竞争关系

      • 我们对进程的三状态时刻关注,因此尽量减少IO操作,或者在进程内开线程来规避IO

      • 让我们写的程序在运行时,能够更多的占用CPU资源

    • 为什么线程之间数据不安全

      • 线程之间数据共享

      • 多线程的情况下:

        • 如果在计算某一个变量时,还要进行赋值操作,这个过程不是由一条完整的CPU指令完成的,

        • 如果在判断某一个bool表达式时,再做某些操作,这个过程也不是由一条完整的CPU指令完成的,

        • 在中间发生了GIL锁的切换或时间片的轮转,可能导致数据不安全

    • 守护线程

      • 守护线程会等待主线程(包括其他子线程)结束之后才结束

      • 守护线程的结束原理,主进程结束之后,守护线程和其他所有线程资源一起被回收掉

    • 线程锁——最好只创建一把锁,线程锁一定锁不了进程

      • 互斥锁:

        • 在同一个线程中,不能连续被acquire多次,一次acquire必须对应一次release

        • 效率高

      • 递归锁

        • 在同一个线程中,可以被连续acquire多次,但一个aquire对应一个release

        • 效率低

      • 死锁现象:

        • 多把锁交替使用,在第一把锁未release释放之前已经被acqiure了

          lock.acquire()

          lock.acquire()

        • 如何解决:

          • 最快的解决办法是:把所有的互斥锁修改成递归锁,影响效率

          • 后期在慢慢整理,把递归锁能解决的问题用互斥锁处理

      • 判断数据是否安全

        • 是否数据共享,是同步还是异步,(数据共享且异步)

        • +=,-=,*=,/=计算之后,数据不安全

        • if while条件这两个判断由多个线程完成,数据不安全

    • 队列queue

      • 线程队列

        • 数据安全

        • 先进先出

        • 原理:加锁+链表

        • 先进先出的队列 Queue

        • 后进先出的队列 FILOQueue 栈

        • 优先级队列priorityQueue 根据放入数据的ascii码从小到大输出

     

    二、今日内容

    1、池 (重要)

    • 线程池

      • 一般更具IO的比例定制

      • 个数:cpu_count * 5

      #线程池
      from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor
      from threading import current_thread
      import time
      import random
      import os
      def func():
         print(current_thread().ident,'开始')
         time.sleep(random.randint(1,4))
         print(current_thread().ident,'end')

      tp = ThreadPoolExecutor(3)  #创建一个池,内部存在三个线程
      for i in range(20):
         tp.submit(func)  #提交线程任务

       

    • 进程池

      • 高计算(没有IO操作,没有文件操作,没有网络操作,没有input)

      • 个数:cup_count * 1 < cup_count * 2

    #进程池
    from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor
    from threading import current_thread
    import time
    import random
    import os
    def func():
       print(os.getpid(),'开始')
       time.sleep(random.randint(1,4))
       print(os.getpid(),'end')

    if __name__ == '__main__':
       tp = ProcessPoolExecutor(3)
       for i in range(20):
           tp.submit(func)
    • 函数有返回值

      #有返回值
      from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor
      from threading import current_thread
      import time
      import random
      import os
      def func(i):
         print(current_thread().ident,'开始')
         time.sleep(random.randint(1,4))
         print(current_thread().ident,'end')
         return i * (i+1)
      tp = ThreadPoolExecutor(3)
      tp_lst = []
      for i in range(20):
         ret = tp.submit(func,i) #异步非阻塞
         tp_lst.append(ret)
      for ret in tp_lst:
         print(ret.result()) #获取函数的返回结果 #同步阻塞
    • 使用回调函数 效率最高

      from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor
      from threading import current_thread
      import time
      import random
      import os
      def func(i):
         print(current_thread().ident,'开始')
         time.sleep(random.randint(1,4))
         print(current_thread().ident,'end')
         return i * (i+1)
      def get_result(ret):  # 回调函数
         print(ret.result())
      tp = ThreadPoolExecutor(3)
      for i in range(20):
         ret = tp.submit(func,i)
         ret.add_done_callback(get_result) #异步阻塞

       

    2、协程

    • 协程概念(非常重要)

      • 是操作系统不可见的

      • 协程本质就是一条线程,多个任务在一条线程上来回切换,来规避IO操作,就达到我们将一条线程中的io操作降到最低

      • 模块 规避io的两个模块

        • gevent 利用了 greenlet 底层模块完成的切换—自动规避io的功能

        • asyncio 利用了 yield 底层语法完成的切换—自动规避io的功能

      • 1611629278250

    • gevent 第三方模块

      • 会用

      • 能处理一些基础的网络操作

        from gevent import monkey
        monkey.patch_all()
        import gevent
        import time
        import random
        def func(i):
           print('start:',i)
           time.sleep(random.randint(1,4))
           print('end:',i)
        g = gevent.spawn(func,1)
        g1 = gevent.spawn(func,1)
        g2 = gevent.spawn(func,1)
        gevent.joinall([g,g1,g2])

         

    • asyncio 内置模块

      import asyncio
      async def func(i):
         print('start:', i)
         await asyncio.sleep(2)
         print('end:', i)

      loop = asyncio.get_event_loop()
      loop.run_until_complete(asyncio.wait([func(1),func(3)]))

       

     

     

    3、区别

    • 进程:计算机资源分配的最小单位 数据不安全 开销大 可以利用多核 操作系统级别

    • 线程 计算机(cpu)调度的最小单位 数据不安全 开销小 不能利用多核 操作系统级别

    • 协程 计算机(cpu)调度的最小单位 数据安全 更小 不能利用多核 用户级别

  • 相关阅读:
    洛谷 1339 最短路
    洛谷 1330 封锁阳光大学 图论 二分图染色
    洛谷 1262 间谍网络 Tarjan 图论
    洛谷 1373 dp 小a和uim之大逃离 良心题解
    洛谷 1972 莫队
    洛谷 2158 数论 打表 欧拉函数
    洛谷 1414 数论 分解因数 水题
    蒟蒻的省选复习(不如说是noip普及组复习)————连载中
    关于筛法
    关于整数划分的几类问题
  • 原文地址:https://www.cnblogs.com/iaoyuyuyuhuanghuang/p/14436485.html
Copyright © 2011-2022 走看看