人生苦短,我用python。
一、协程
协程,又称微线程,纤程。英文名Coroutine。一句话说明什么是线程:协程是一种用户态的轻量级线程。
(1)比较专业的理解是:
协程拥有自己的寄存器上下文和栈。协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈。因此:协程能保留上一次调用时的状态(即所有局部状态的一个特定组合),每次过程重入时,就相当于进入上一次调用的状态,换种说法:进入上一次离开时所处逻辑流的位置。
(2)这么来理解协程比较容易:
线程是系统级别的,它们是由操作系统调度;协程是程序级别的,由程序员根据需要自己调度。我们把一个线程中的一个个函数叫做子程序,那么子程序在执行过程中可以中断去执行别的子程序;别的子程序也可以中断回来继续执行之前的子程序,这就是协程。
(3)协程的优点:
- 无需线程上下文切换的开销,协程避免了无意义的调度,由此可以提高性能(但也因此,程序员必须自己承担调度的责任,同时,协程也失去了标准线程使用多CPU的能力)
- 无需原子操作锁定及同步的开销
- 方便切换控制流,简化编程模型
- 高并发+高扩展性+低成本:一个CPU支持上万的协程都不是问题。所以很适合用于高并发处理。
(4)协程的缺点:
- 无法利用多核资源:协程的本质是个单线程,它不能同时将 单个CPU 的多个核用上,协程需要和进程配合才能运行在多CPU上.当然我们日常所编写的绝大部分应用都没有这个必要,除非是cpu密集型应用。
- 进行阻塞(Blocking)操作(如IO时)会阻塞掉整个程序
二、yield实现协程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| import time
def A(): while True: print("----A---") yield time.sleep(0.5)
def B(c): while True: print("----B---") next(c) time.sleep(0.5)
if __name__=='__main__': a = A() B(a)
|
三、greenlet模块实现协程
python中的greenlet模块对其封装,从而使得切换任务变的更加简单,greenlet就相当于手动切换,去执行别的子程序,在“别的子程序”中又主动切换回来
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| from greenlet import greenlet import time
def test1(): while True: print('----A----') gr2.switch() time.sleep(0.5) def test2(): while True: print('----B----') gr1.switch() time.sleep(0.5) gr1 = greenlet(test1) gr2 = greenlet(test2)
gr1.switch()
|
四、gevent模块实现协程
greenlet已经实现了协程,但是这个还要人工切换,python还有一个比greenlet更强大的并且能够自动切换任务的模块gevent。
其原理是当一个greenlet遇到IO(指的是input output输入输出,比如网络、文件操作等)操作时,比如访问网络,就自动切换到其他的greenlet,等到IO操作完成,再在适当的时候切换回来继续执行。
由于IO操作非常耗时,经常使程序处于等待状态,有了gevent为我们自动切换协程,就保证总有greenlet在运行,而不是等待IO。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
| import gevent
def f(n): for i in range(n): print(gevent.getcurrent(),i) g1 = gevent.spawn(f,5) g2 = gevent.spawn(f,5) g3 = gevent.spawn(f,5) g1.join() g2.join() g3.join()
import gevent
def f(n): for i in range(n): print(gevent.getcurrent(),i) gevent.sleep(1) g1 = gevent.spawn(f,5) g2 = gevent.spawn(f,5) g3 = gevent.spawn(f,5) g1.join() g2.join() g3.join()
import gevent from gevent import monkey from urllib import request
monkey.patch_all()
def myDownLoad(url): print('GET: %s' % url) res = request.urlopen(url) data = res.read() print('%d bytes received from %s.' % (len(data), url)) gevent.joinall([ gevent.spawn(myDownLoad, 'http://www.baidu.com/'), gevent.spawn(myDownLoad, 'http://www.itcast.cn/'), gevent.spawn(myDownLoad, 'http://www.itheima.com/'), ])
|
持续更新…