10月17日学习内容整理:协程
一、协程的基本性质
1、概念:协程是由用户程序自己控制调度的,是一种用户态的轻量级线程
2、作用:用来实现单线程下实现并发,从应用程序级别控制单线程下多任务的切换,注意一定是遇到IO才切且不可能有并行的概念
3、优缺点
(1)优点:
》协程的切换速度相比操作系统切换速度要小得多
》隐藏自己的IO阻塞,使程序尽可能的处在就绪态,争取更多的执行权限,来“骗”操作系统识别不到线程的IO操作
(2)缺点:
》无法利用多核优势
》一旦协程出现不可挽回的IO阻塞(因为不可能真正的隐藏IO阻塞),会阻塞整个线程
4、特点:
》必须在单线程的前提下
》修改共享数据不用加锁
二、代码实现协程
1、greenlet模块(第三方库)
(1)方法
g=greenlet(函数名) 得到一个协程对象
g.switch(参数) 切换到g去执行,第一次切的时候传参数,后面再切换就不用传参了
(2)特点
但是,遇到IO阻塞是无法自动切换的,但是比yield切换更方便点
#greenlet与yield一样都无法检测io然后自动切换,唯一一点好处是:greenlet比yieled的切换方式更加方便 from greenlet import greenlet import time def eat(name): print('%s eat 1' %name) time.sleep(10) g2.switch('alex') print('%s eat 2' %name) g2.switch() def play(name): print('%s play 1' %name) g1.switch() print('%s play 2' %name) g1=greenlet(eat) g2=greenlet(play) g1.switch('alex')
2、gevent模块(第三方库):真正实现切换的效果
(1)作用:能够做到自己检测IO,并实现自动切换
(2)方法:
g=gevent.spawn(函数名,参数) 得到一个协程对象,异步提交任务
g.join() 等到g结束
gevent.joinall([g1,g2])等g1,g2都结束,注意是列表形式
g.value 取g的运行结果,注意要join后或者是运行完后才能拿结果,spawn后立刻取结果可能是None
gevent.sleep() 也是睡几秒,相当于IO阻塞
gevent只能直接识别自己的IO阻塞,若是time.sleep()就不会直接识别也就不会自动切换,要想解决这个问题,就必须在导入gevent之前先加补丁:
from gevent import monkey
monkey.patch_all()
或者
from gevent import monkey;monkey.patch_all()
这样协程就能识别所有的IO阻塞
from gevent import monkey;monkey.patch_all() #这样就可以识别所有的IO阻塞 import gevent import os from threading import current_thread #1.检测IO #2.自动切换 import time def eat(): print('%s eat 1' %current_thread().getName()) time.sleep(2) print('%s eat 2' %current_thread().getName()) def play(): print('%s play 1' %current_thread().getName()) time.sleep(1) print('%s play 2' %current_thread().getName()) start=time.time() g1=gevent.spawn(eat,) g2=gevent.spawn(play,) # g1.join() # g2.join() gevent.joinall([g1,g2]) stop=time.time() print(stop-start)
3、基于协程的单线程并发
客户端:
from threading import Thread from socket import * def client(): c=socket(AF_INET,SOCK_STREAM) c.connect(('127.0.0.1',8092)) while True: c.send('hello'.encode('utf-8')) data=c.recv(1024) print(data.decode('utf-8')) if __name__ == '__main__': for i in range(500): t=Thread(target=client) t.start()
服务端:
from gevent import monkey;monkey.patch_all() import gevent from multiprocessing import Process from socket import * def server(ip,port): s = socket(AF_INET, SOCK_STREAM) s.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1) s.bind((ip,port)) s.listen(5) while True: conn,addr=s.accept() print('%s:%s' % (addr[0], addr[1])) g1=gevent.spawn(talk,conn,addr) def talk(conn,addr): while True: try: data=conn.recv(1024) print('%s:%s [%s]' %(addr[0],addr[1],data)) if not data:break conn.send(data.upper()) except ConnectionResetError: break conn.close() if __name__ == '__main__': server('127.0.0.1',8092)