一. 协程
协程: 是单线程下的并发,又称微线程,纤程,英文名Coroutine.
并发: 切换+保存状态
协程是一种用户态的轻量级线程,即协程是由用户程序自己控制调度的
协程特点:
1. 必须在只有一个单线程里实现并发
2. 修改共享数据不需要加锁
3. 用户程序自己保存多个控制流的上线文栈
4. 一个协程遇到IO操作自动切换到其他协程(如何实现检测IO,yield,greenlet都无法实现,
就用到了gevent模块(select机制))
Greenlet模块
单纯的切换(在没有IO的情况下或者没有重复开辟内存空间的操作),反而会降低程序的执行速度
#真正的协程模块就是使用greenlet完成的切换 from greenlet import greenlet def eat(name): print('%s eat 1' %name) #2 g2.switch('taibai') #3 print('%s eat 2' %name) #6 g2.switch() #7 def play(name): print('%s play 1' %name) #4 g1.switch() #5 print('%s play 2' %name) #8 g1=greenlet(eat) g2=greenlet(play) g1.switch('taibai')#可以在第一次switch时传入参数,以后都不需要 1
#安装 pip install greenlet
1 # import time 2 # 3 # import greenlet 4 # def fun1(): 5 # time.sleep(2) 6 # print('约吗?') 7 # g2.switch() 8 # time.sleep(2) 9 # print('不约') 10 # g2.switch() 11 # def fun2(): 12 # time.sleep(2) 13 # print('你好') 14 # g1.switch() 15 # time.sleep(2) 16 # print('你不好') 17 # 18 # g1 = greenlet.greenlet(fun1) 19 # g2 = greenlet.greenlet(fun2) 20 # g1.switch() 21 # ---------------------------------------------------- 22 # import time 23 # import greenlet 24 # def f1(name): 25 # print(name) 26 # time.sleep(2) 27 # a = g2.switch(name+'1') 28 # print(a) 29 # 30 # def f2(name): 31 # print(name) 32 # time.sleep(2) 33 # g1.switch(name+'1') 34 # 35 # a = time.time() 36 # g1 = greenlet.greenlet(f1) 37 # g2 = greenlet.greenlet(f2) 38 # g1.switch('hui') 39 # b = time.time() 40 # print(b-a)
Gevent模块
#安装 pip install gevent
#用法 g1 = gevent.spawn(func,多个参数) 创建一个协程对象g1, spawn(函数名,多个参数) spawn是异步提交任务 g2 = gevent.spawn(func2) g1.join() #等待g1结束,上面只是创建协程对象,这个join才是去执行
g2.join() #等待g2结束,
gevent.joinall([g1,g2]) #等待列表中的协程都结束
g1.value #拿到func1的返回值
gevent.sleep(2)# 睡2秒 遇到IO切换
# 不能识别time.sleep 解决办法 from gevent import monkey;monkey.patch_all()
遇到IO阻塞是自动切换任务
import gevent def eat(name): print('%s eat 1' %name) gevent.sleep(2) print('%s eat 2' %name) def play(name): print('%s play 1' %name) gevent.sleep(1) print('%s play 2' %name) g1=gevent.spawn(eat,'egon') g2=gevent.spawn(play,name='egon') g1.join() g2.join() #或者gevent.joinall([g1,g2]) print('主')
gevent.sleep(2)模拟的是gevent可以识别的io阻塞,
而time.sleep(2)或其他的阻塞,gevent是不能直接识别的需要下面一行的代码,打补丁,就可以识别
from gevent import monkey;monkey.patch_all()必须放到被打补丁者的前面,如time,socket模块之前
或者直接记忆成: 要用gevent, 需要将from gevent import monkey;monkey.patch_all()放到文件的开头
1 # import gevent 2 # import time 3 # #gevent 不能识别 time.sleep 4 # # 解决办法 from gevent import monkey;monkey.patch_all() 识别所有的IO 自动切换 5 # 6 # def func(n,): 7 # print('xxxx',n) 8 # gevent.sleep(2) 9 # # time.sleep(2) #不能识别 10 # print('ccccc') 11 # 12 # def func2(m): 13 # print('11111111',m) 14 # gevent.sleep(2) 15 # print('22222222') 16 # g1 = gevent.spawn(func,'alex') # 异步提交任务 17 # g2 = gevent.spawn(func2,'马来西亚') 18 # 19 # # gevent.joinall([g1,g2]) #等多所有 20 # g1.join() #执行并等待异步提交的任务完成 21 # g2.join()#执行并等待异步提交的任务完成 22 # print('代码结束')
二. IO多路复用
IO模型
#1) 等待数据准备(Waiting for the data to be ready) #2) 将数据从内核拷贝到进程中(Copying the data from the kernel to the process)
1. 阻塞IO
2. 非阻塞IO模型
3. IO多路复用
4. 异步IO