协程(gevent、asyncio)

协程

  • 协程是操作系统不可见的

  • 什么是协程?

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

    协程(gevent、asyncio)

gevent

  • 第三方模块

    • gevent,利用了底层模块(greelet)完成切换 + 自动规避IO的功能
    • gevent模块不能规避所有IO,只能规避一部分,查看方法:
      1. patch_all()之前打印一次,patch_all()之后打印一次,如果不一样则规避了,如果一样则没有规避
      2. 直接通过按住ctrl点击patch_all()查看源码为True的可以规避,为Flase的不能规避
  • 代码:

    • #方式一:
      import gevent
      def func():  #所有带有IO操作的内容都写在函数里,然后提交function
          print('start func')
          gevent.sleep(1)
          print('end func')
      g1=gevent.spawn(func)
      g2=gevent.spawn(func)
      g3=gevent.spawn(func)
      # gevent.sleep(2)
      # g1.join()  #一直阻塞,直到协程g1任务执行结束
      # g2.join()
      # g3.join()
      gevent.joinall([g1,g2,g3])	
      
      #方式二 *********
      from gevent import monkey
      monkey.patch_all()  #此两句必须写在所有导入模块的最上面,否则报错
      import gevent
      import time
      def func():  #所有带有IO操作的内容都写在函数里,然后提交function
          print('start func')
          time.sleep(1)
          print('end func')
      g1=gevent.spawn(func)
      g2=gevent.spawn(func)
      g3=gevent.spawn(func)
      time.sleep(2)
      # g1.join()  #一直阻塞,直到协程g1任务执行结束
      # g2.join()
      # g3.join()
      # gevent.joinall([g1,g2,g3])
      
  • 基于gevent协程实现Socket的并发

    • server

      from gevent import monkey
      monkey.patch_all()
      import gevent
      import socket
      
      sk=socket.socket()
      sk.bind(('127.0.0.1',1235))
      sk.listen()
      
      def func(conn):
          while True:
              msg=conn.recv(1024).decode('utf-8')
              MSG=msg.upper()
              conn.send(MSG.encode('utf-8'))
      
      while True:
          conn,addr=sk.accept()
          gevent.spawn(func,conn) #spawn开启任务
      
    • client

      import socket
      
      sk=socket.socket()
      sk.connect(('127.0.0.1',1235))
      
      
      while True:
          ret=input('请输入:')
          sk.send(ret.encode('utf-8'))
          mag=sk.recv(1024).decode('utf-8')
          print(mag)
      

asyncio

  • 原生的内置模块

    • asyncio,利用了底层模块(yield)完成切换 + 自动规避IO的功能

    • aiohttp模块:基于asyncio实现的,用来做并发的爬虫

    • sanic异步的轻量级的Web框架中,基于asyncio实现的

      • async:函数前面有async就代表是一个协程函数
      • await:await后面是可能会发生阻塞的方法,await关键字必须写在async函数里面
    • 代码

      import asyncio
      async def func(name):
          '''
          await 可能会发生阻塞的方法
          await关键字必须写在async函数里面
          '''
          print('start',name)
          await asyncio.sleep(1)
          print('end')
      loop=asyncio.get_event_loop()
      # loop.run_until_complete(func('xiaotong')) #调用一次
      loop.run_until_complete(asyncio.wait([func('xiaotong'),func('minmin')])) #调用多次
      

协程的实现原理

  • 生成器(Yield)中遇到yield的时候会直接切到函数外面,等到在此调用next的时候在执行下面的代码

  • 协程(gevent、asyncio)

  • 代码

    import time
    
    def sleep(n):
        print('start sleep')
        yield time.time()+n
        print('end sleep')
    def func(n):
        print('start')
        g=sleep(n)
        yield from g   # yield from 相当于 await
        print('end')
    
    g1=func(1)
    g2=func(1.1)
    
    def run_until_complete(g1,g2): #run_until_complete就相当于run_until_complete
        ret1=next(g1) #睡完的时间
        ret2=next(g2) #睡完的时间
        ret_list={ret1:g1,ret2:g2}
        while ret_list:
            min_time=min(ret_list) #获取字典中睡眠最小的值
            time.sleep(min_time-time.time())
            try:
                next(ret_list[min_time]) #睡眠过后再次调用next
            except StopIteration:pass
            del ret_list[min_time]
    run_until_complete(g1,g2)
    
    
    #得:start
    start sleep
    start
    start sleep
    end sleep
    end
    end sleep
    end
    
    
上一篇:Python3 协程


下一篇:python中split方法部分切割