前面讲的两个协程的用途,一个是用来使用协程表达工作流里的流程的概念,一个是用协程来表达一个动画的播放过程。总结起来,就是用协程来解决带有流程阻塞的代码逻辑组织的问题。但是协程并不是生来干这个的,协程最主流的用途是一种用于处理I/O阻塞的工具。I/O阻塞的挑战是多重的,一方面是有很多人研究高并发低延迟之类的问题,另外一方面I/O阻塞对于代码逻辑的组织挑战也是巨大的。比如最经典的基于回调的I/O阻塞编码风格,下面是一个使用Twisted框架的代码示例(摘自:https://github.com/feihong/tulip-talk/blob/master/examples):
from twisted.internet import reactor
from twisted.internet.defer import Deferred, succeed
from twisted.internet.protocol import Protocol
from twisted.web.client import Agent
def print_headers(response):
for k, v in response.headers.getAllRawHeaders():
print('{}: {}'.format(k, v[0][:80]))
return get_response_body(response)
def get_response_body(response):
class BodyReceiver(Protocol):
def dataReceived(self, data):
chunks.append(data)
def connectionLost(self, reason):
finished.callback(''.join(chunks))
finished = Deferred()
chunks = []
response.deliverBody(BodyReceiver())
return finished
def print_body(data):
print('\nReceived {} bytes.\n'.format(len(data)))
return succeed(None)
if __name__ == '__main__':
agent = Agent(reactor)
d = agent.request('GET', 'http://megafeihong.tumblr.com')
d.addCallback(print_headers)
d.addCallback(print_body)
d.addCallback(lambda x: reactor.stop())
reactor.run()
代码逻辑是不是非常不好懂?究其原因在于一个顺序的执行过程,因为中间的I/O阻塞被拆成了意大利面条式的破碎逻辑。而是用协程就可以很好的实现Logic Locality,把上下相关的代码放在一段函数内:
import tulip
from tulip import http
@tulip.coroutine
def download(url):
response = yield from http.request('GET', url)
for k, v in response.items():
print('{}: {}'.format(k, v[:80]))
data = yield from response.read()
print('\nReceived {} bytes.\n'.format(len(data)))
if __name__ == '__main__':
loop = tulip.get_event_loop()
coroutine = download('http://omegafeihong.tumblr.com')
loop.run_until_complete(coroutine)
这段代码是不是容易懂多了?这个框架叫tulip,是Python基于协程,确切说是基于generator的网络异步I/O编程框架。其中使用了一个新的yield from语法,暂时不用深究,基本上和yield是一个意思,代表函数在这个地方放弃执行,让外部去把I/O处理完,等I/O操作完成了再从上次放弃执行的地方继续执行后面的语句。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。