python之协程
一、这几天在折腾Python的协程问题,Python的协程相对原理上来说要简单很多了。在使用Java做开发的时候,经常使用线程,过程中经常也有听说过微线程/协程的概念,但是没有去深刻的学习过Java的协程是怎么实现的。这一篇文章主要是讲述Python中的协程原理。后面补上Java中的协程原理!
二、前面我们基本大概了解了多线程和多进程的问题,在资源的占用上面,多进程比多线程占用的多。我们这这里通过cpu调度最小的单位线程来说:
1)多线程:多线程的调用需要cpu分配时间片然后来回的切换,并且是随机的。
2)协程:协程是在单线程中的多个任务同时执行的相互配合,避免了线程之间切换造成的资源浪费
3)意义:当我们在使用多线程的时候,如果存在长时间的I/O操作。这个时候线程一直处于阻塞状态,如果线程很多的时候,会存在很多线程处于空闲状态,造成了资源应用不彻底。相对的协程不一样了,在单线程中多个任务来回自行如果出现长时间的I/O操作,让其让出目前的协程调度,执行下一个任务。当然可能所有任务,全部卡在同一个点上,但是这只是针对于单线程而言,当所有数据正常返回时,会同时处理当前的I/O操作。
4)作用:用更小的资源做更多地操作,更加高效的利用线程。当然,也有问题点,如果存在计算密集型的操作,协程起到的效果不是那么大。
5)理解:协程不是真正意义上的像线程一样开辟空间(比如一个线程在64位系统下,stack大小为1024KB),协程没有,只是在单线程中,多个任务来回切换造成的多任务执行的假象。
三、实现原理
1)基本原理:是用过yield的方式来实现,来阻塞当前执行,等到下一个next()来了过后接着执行。
yield:生成器的关键字,通过暂停并返回数据,达到产生数据的效果。
2)代码理解:
import time def show_a(): while True: print "---A---" yield time.sleep(0.5) def show_b(c): while True: print "---B---" c.next() time.sleep(0.5) if __name__ == '__main__': # 生成器 a = show_a() # 调用方法show_b show_b(a)
3)测试结果:
4)解释:
a、协程是通过生成器的方式来达到效果的。
b、目的让多个任务来回/同时执行
四、通过greenlet实现协程
from greenlet import greenlet def test1(): while True: print "----A----" # 切换 gr2.switch() def test2(): while True: print "----B----" # 切换 gr1.switch() gr1 = greenlet(test1) gr2 = greenlet(test2) if __name__ == '__main__': gr1.switch()
效果:
这样达到的效果就是多任务执行
解释:
这里的switch()方法就是yield的效果
五、通过gevent达到协程的效果,这里通过socket连接阻塞的例子来演示:
from gevent import socket, monkey # 用于动态加入yield monkey.patch_all() def request_handle(cli, address): while True: # 等待接收数据,阻塞 data = cli.recv(1024) if len(data) > 0: print data else: print "close" + str(address) cli.close break def server(port): s = socket.socket() s.bind(('', port)) s.listen(5) while True: # 等待接入,阻塞 cli, address = s.accept() print address # 新建一个协程 gevent.spawn(request_handle, cli, address) def server_test(): while True: print "test" gevent.sleep(1) if __name__ == '__main__': # 为了独立运行,加入2个协程任务 s1 = gevent.spawn(server, 9000) # 用于展示独立性 s2 = gevent.spawn(server_test) s1.join() s2.join()
效果:
注意:这里的yield是自动加入的,目的是自动让出时间执行过长的任务,到可以执行的任务中区执行。
阻塞为一种长时间的等待,当有新的连接进来,才会执行请求任务。
这里客户端使用telnet测试。
六、总结:Python的协程原理是通过yield关键字,并且通过生成器的方式来达到多任务的执行。对于I/O密集型的操作,可以采用协程方式来做。如果存在计算密集型(占用cpu的操作很高),不要使用协程,最好使用多进程。
个人对于协程的理解,基本上就是这些,如果存在错误的地方还请指出。