Python 协程
来源:互联网 发布:李叫兽真的是大神知乎 编辑:程序博客网 时间:2024/04/29 03:29
在协程之前我们有什么?
协程实际上不是一个新概念,作为一个并发模型,在很早以前就能看到协程的身影。只是最近才开始变得火热起来,因为它可以很好的处理IO密集型任务,而这符合互联网行业的业务需求。
在我们重新认识协程之前,先简短回顾下几个常用的并发模型。
最简单的就是串行执行的程序,遇到某一个IO事件就会阻塞直到IO事件返回,这种程序最大的缺点就是慢,耗时约等于全部IO耗时与计算耗时之和。
为了提升运行效率,可以采用多线程或多进程模型,因为IO事件实际上是不消耗计算资源的,只需要等待而已,所以在IO事件等待的时候切换到另一个任务来提升运行速度,而这需要依靠操作系统对线程和进程的调度。
但是线程和进程的分配是需要开销的,在面对大量IO事件时,系统资源就不够用了,所以才有了 select, poll, epoll等非阻塞异步IO。但是异步程序虽然性能上非常棒,但是可读性上十分反人类,因为它对原本连贯的逻辑进行了拆分,在系统规模变得很大时,我们很难理解系统的逻辑。而协程在一定程度上可以解决这个问题,它可以在保持程序的逻辑连贯性的同时,通过对任务的调度来实现调用的异步性,同时协程由于整个程序都在一个线程内,所以上下文切换的开销极小,运行效率极高。
Python 中的协程
生成器
生成器是 Python 中的一个重要特性,在 Python2 里面协程需要在生成器的基础上进行拓展。所以先来看一个生成器的例子,这个例子是一个将大文件分块读入内存的例子,它避免了文件过大无法一次性读入内存,或者是一次性读入太慢的问题。常用的 range 也有一个生成器版 xrange,range 一次性会生成所有的数据,而 xrange则到需要时才生成。
import osdef chunked_file(file, n=100): with open(file) as fp: while True: chunk = fp.read(n) if chunk: yield chunk.encode('utf-8') else: break fp.seek(n, os.SEEK_CUR)if __name__ == '__main__': for chunk in chunked_file('demo.txt', 256): print chunk
除了上面的用法,生成器还可以主动调用 next 方法获取下一个输出,在没有下一个输出的情况下会抛出 StopIteration , 前面的for循环只是一个语法糖帮我们处理了next和对抛出异常的检测。
>>> demo = chunked_file('demo.txt', 10)>>> demo.next()'1234567890'>>> demo.next()'1234567890'...>>> demo.next()StopIteration:
传递数据
Python 里为生成器提供了一个 send 方法,用来向生成器发送数据。下面是一个echo函数的例子,同时可以通过调用生成器函数的close方法来关闭生成器。
# echo.pydef echo(): x = yield while True: x = yield x
>>> generator = echo()>>> generator.send(None) # 启动生成器>>> generator.send('hello')'hello'>>> generator.send('world')'world'>>> generator.close() # 关闭生成器
使用 yield 和 send 构建协程
有了这两个基础的语义,就可以在此基础上构建协程了。下面是一个简单的协程调度实现,通过对函数的切换达到并发执行的效果。
# -*- encoding=utf-8 -*-import Queuedef countdown(n): while n > 0: print '[Counting Down {n}...]'.format(n=n) n -= 1 yielddef countup(n): x = 0 while x < n: print '[Counting Up {n}...]'.format(n=x) x += 1 yielddef scheduler(fn_list=(countdown(10), countdown(5), countup(5))): queue = Queue.deque() queue.extend(fn_list) while len(queue): try: task = queue.popleft() task.send(None) queue.append(task) except StopIteration: passif __name__ == '__main__': scheduler()
输出结果:
[Counting Down 10...][Counting Down 5...][Counting Up 0...][Counting Down 9...][Counting Down 4...][Counting Up 1...][Counting Down 8...][Counting Down 3...][Counting Up 2...]......
参考链接
编程珠玑番外篇-Q 协程的历史,现在和未来
- python 协程
- python协程
- python协程
- python 协程
- python 协程
- python 协程
- python 协程
- python协程
- python 协程
- Python 协程
- Python-协程
- Python 协程
- Python 协程
- python协程
- python协程
- python 协程
- python 协程
- python协程
- 经典SQL语句大全
- 《大话设计模式》之大总结
- Recyclerview的一些个人理解与使用(四)在界面中实现侧边栏效果
- java
- wampserver 访问 403 解决办法
- Python 协程
- java堆栈,基本数据存储方式和Integer缓存造成的小小问题
- 剑指offer 41. 和为s的两个数字VS和为s的连续正数序列
- Android EditText不为空时Button才可点击
- MySQL用通配符进行过滤
- Java代码实现对properties文件有序的读写
- spring框架之aop
- svn 忽略文件夹,svn忽略版本控制
- 群体遗传学瓶颈效应bottleneck effect