13、协程

一、概念

  1. 定义
    • 协程本质上就是一条线程
    • 协程可以把多个任务在一条线程来回切换
    • 利用协程的特性可以把IO操作时的等待时间也利用起来,降低IO时间
  2. 切换并规避IO的模块
    • gevent
    • asyncio
  3. 进程、线程、协程
    • 进程:数据隔离,数据不安全,开销很大,可以利用多核
      • 由操作系统控制的
    • 线程:数据共享,数据不安全,开销比较小,不能利用多核
      • 线程是CPU层面控制的,有GIL锁,导致数据不安全
    • 协程:数据共享,数据安全,开销最小,不能利用多核
      • 由用户自己控制的,什么时候开始轮转GIL锁

二、协程——gevent模块

  1. 协程与线程、进程不同之处

    • 主线程(主进程)会等待所有的子线程(子进程)执行结束才会结束
    • 主线程不会等待协程结束,当主程序结束之后所有的协程自动结束。,可以使用join或者joinall来阻塞主线程等待所有协程执行结束。
  2. 例子

    • 要先安装gevent模块

    • from gevent import monkey
      monkey.patch_all()

      这两个语句一定要放在最上面,不然可能出错

    from gevent import monkey 
    monkey.patch_all()
    import time
    import gevent
    
    def func():  #带有IO操作的内容写在func函数中
        print('start func')
        time.sleep(1)
        print('end func')
    li=[]
    for i in range(40):
        g=gevent.spawn(func)  #把func函数交给gevent.spawn执行
        li.append(g)
    gevent.joinall(li)     #制造阻塞,等所有协程执行完毕
    
  3. 等待所有的协程支持结束

    • join:阻塞一个

      
      
    • joinall:阻塞全部

  4. 利用gevent模块,实现socket并发

    • from gevent import monkey
      monkey.patch_all()

      gevent默认识别不到系统级别的IO操作,这两句话作用让gevent识别到所有的IO

    • func函数就是一个协程处理函数

    from gevent import monkey
    monkey.patch_all()
    import gevent
    import socket
    
    def func(conn):
        while True:
            ret=conn.recv(1024).decode('utf-8')
            ret=ret.upper()
            conn.send(ret.encode('utf-8'))
    
    sk=socket.socket()
    sk.bind(('127.0.0.1',9000))
    sk.listen()
    
    #可以一直连接,每次连接之后都交给协程处理
    while True:
        conn,_=sk.accept()
        gevent.spawn(func,conn)
    
    • 检测一个模块是否可以规避掉IO操作

      import socket
      print(socket.socket)
      
      from gevent import monkey
      monkey.patch_all()
      import socket
      print(socket.socket)
      
      #结果如下
      <class 'socket.socket'>
      <class 'gevent._socket3.socket'>
      
      • 两次结果不一样,表示gevent模块可以规避掉该IO操作

三、协程——asyncio模块

posted @ 2023-07-12 09:52  majic_jie  阅读(15)  评论(0)    收藏  举报