• python网络编程 day35 网络编程——进程池,线程池、协程、回调函数、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)调度的最小单位 数据安全 更小 不能利用多核 用户级别

  • 相关阅读:
    javascript高级实战学习
    javascript基础入门知识点整理
    Spring Boot 配置文件application.properties
    Spring Boot应用启动器
    IDEA安装maven和设置自动配置下载包
    Spring Data Solr创建动态域报错:org.springframework.data.solr.UncategorizedSolrException
    虚拟机使用配置固定IP
    1.Solr安装与配置
    spring mvc 常用注解
    Jqgrid学习
  • 原文地址:https://www.cnblogs.com/iaoyuyuyuhuanghuang/p/14332522.html
Copyright © 2020-2023  润新知