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)调度的最小单位 数据安全 更小 不能利用多核 用户级别

posted @ 2021-01-26 21:27  小鱼鱼与黄黄  阅读(94)  评论(0编辑  收藏  举报