python2异步编程_理解 Python 的异步编程3

Python部落(python.freelycode.com)组织翻译,禁止转载,欢迎转发。

例6:协程异步(非阻塞)模式的 HTTP 下载

这次我们把协程的方案也放进来。还记得我刚才说过,使用协程库里的 monkey.patch_all() 方法可以把任何同步代码转换为异步,当然也包括这个任务里用到的 requests 库。

现在 requests.get(url) 函数可以通过协程的事件循环来切换上下文,转换成非阻塞模式了,也就不用写 yield 了。在任务执行部分,我们使用协程来启动两个任务,最后用 joinall() 方法等待它们执行完毕。

"""

example_6.py

Just a short example demonstrating a simple state machine in Python

This version is doing actual work, downloading the contents of

URL's it gets from a queue. It's also using gevent to get the

URL's in an asynchronous manner.

"""

import gevent

from gevent import monkey

monkey.patch_all()

import queue

import requests

from lib.elapsed_time import ET

def task(name, work_queue):

while not work_queue.empty():

url = work_queue.get()

print(f'Task {name} getting URL: {url}')

et = ET()

requests.get(url)

print(f'Task {name} got URL: {url}')

print(f'Task {name} total elapsed time: {et():.1f}')

def main():

"""

This is the main entry point for the program

"""

# create the queue of 'work'

work_queue = queue.Queue()

# put some 'work' in the queue

for url in [

"http://google.com",

"http://yahoo.com",

"http://linkedin.com",

"http://shutterfly.com",

"http://mypublisher.com",

"http://facebook.com"

]:

work_queue.put(url)

# run the tasks

et = ET()

tasks = [

gevent.spawn(task, 'One', work_queue),

gevent.spawn(task, 'Two', work_queue)

]

gevent.joinall(tasks)

print()

print(f'Total elapsed time: {et():.1f}')

if __name__ == '__main__':

main()

"""

译者注,运行结果:

Task One getting URL: http://google.com

Task Two getting URL: http://yahoo.com

Task Two got URL: http://yahoo.com

Task Two total elapsed time: 1.7

Task Two getting URL: http://linkedin.com

timeout

Task One got URL: http://google.com

Task One total elapsed time: 2.0

Task One getting URL: http://shutterfly.com

Task Two got URL: http://linkedin.com

Task Two total elapsed time: 1.2

Task Two getting URL: http://mypublisher.com

Task Two got URL: http://mypublisher.com

Task Two total elapsed time: 2.9

Task Two getting URL: http://facebook.com

Task One got URL: http://shutterfly.com

Task One total elapsed time: 5.7

timeout

Task Two got URL: http://facebook.com

Task Two total elapsed time: 2.0

Total elapsed time: 7.9

"""

仔细看看结尾的总耗时和每个网址分别耗时,显尔易见,总耗时小于每个网址的单独耗时之和。

因为每个请求都是异步的,这就帮助我们有效的利用了 CPU 资源来同时处理多个请求。

例7:基于 Twisted 的异步(非阻塞) HTTP 下载

下面这个例子我们改用 Twisted 框架 来实现刚才协程模块的任务,用非阻塞模式下载网页内容。

Twisted 非常强大,它采用完全不同的方法来创建异步程序。协程用修改模块的方法将同步转换为异步,Twisted 则提供了自己的一套函数和方法来实现同样的效果。

例6 中是用修补 requests.get(url) 的方式获取页面,现在我们改用 Twisted 提供的 getPage(url) 方法。

在下面的代码中,装饰器 @defer.inlineCallbacks 和 yield getPage(url) 一起使用,是为了将上下文切换到 Twisted 的事件循环当中。

在协程中事件循环是隐式的,而在 Twisted 中是通过程序结尾的 reactor.run() 语句来显式调用的。

"""

example_7.py

Just a short example demonstrating a simple state machine in Python

This version is doing actual work, downloading the contents of

URL's it gets from a work_queue. This version uses the Twisted

framework to provide the concurrency

"""

from twisted.internet import defer

from twisted.web.client import getPage

from twisted.internet import reactor, task

import queue

from lib.elapsed_time import ET

@defer.inlineCallbacks

def my_task(name, work_queue):

try:

while not work_queue.empty():

url = work_queue.get()

print(f'Task {name} getting URL: {url}')

et = ET()

yield getPage(url)

print(f'Task {name} got URL: {url}')

print(f'Task {name} total elapsed time: {et():.1f}')

except Exception as e:

print(str(e))

def main():

"""

This is the main entry point for the program

"""

# create the work_queue of 'work'

work_queue = queue.Queue()

# put some 'work' in the work_queue

for url in [

b"http://google.com",

b"http://yahoo.com",

b"http://linkedin.com",

b"http://shutterfly.com",

b"http://mypublisher.com",

b"http://facebook.com"

]:

work_queue.put(url)

# run the tasks

et = ET()

defer.DeferredList([

task.deferLater(reactor, 0, my_task, 'One', work_queue),

task.deferLater(reactor, 0, my_task, 'Two', work_queue)

]).addCallback(lambda _: reactor.stop())

# run the event loop

reactor.run()

print()

print(f'Total elapsed time: {et():.1f}')

if __name__ == '__main__':

main()

"""

译者注,运行结果:

Task One getting URL: b'http://google.com'

Task Two getting URL: b'http://yahoo.com'

Task Two got URL: b'http://yahoo.com'

Task Two total elapsed time: 2.1

Task Two getting URL: b'http://linkedin.com'

Task Two got URL: b'http://linkedin.com'

Task Two total elapsed time: 1.2

Task Two getting URL: b'http://shutterfly.com'

Task Two got URL: b'http://shutterfly.com'

Task Two total elapsed time: 4.2

Task Two getting URL: b'http://mypublisher.com'

Task Two got URL: b'http://mypublisher.com'

Task Two total elapsed time: 5.0

Task Two getting URL: b'http://facebook.com'

User timeout caused connection failure.

User timeout caused connection failure.

Total elapsed time: 42.5

"""

这个运行结果和前面用协程方法得到的一样,总耗时小于每个网址访问单独耗时的总和。

例8:基于 Twisted 回调方法的异步(非阻塞)HTTP 下载

这个例子我仍然使用 Twisted 这个库,只不过换了一个比较传统的方式。

比如说,上面我们采用了 @defer.inlineCallbacks / yield的编码形式,现在我们要改成显式回调。所谓回调函数就是一个传递给系统的用来响应某个事件函数。下面例子中的 success_callback() 函数就是传递给 Twisted 用来响应 getPage(url) 事件的回调函数。

注意,这个例子里的 my_task() 任务函数已经不再需要 @defer.inlineCallbacks 装饰器了。另外,这个函数还生成了一个延迟变量,我们简称它为 d ,它是 getPage(url) 函数的返回值。

延迟变量是 Twisted 处理异步编程的方式,也是回调函数所必需的。一旦延迟被“触发”(也就是getPage(url) 完成时),就立刻调用回调函数,并传入指定参数。

程序运行结果和上面两个示例一样,总耗时小于每个访问分别耗时之和。

你想用协程还是Twisted的方式来实现都可以,因人而异。两种方案都提供了强大的功能帮助开发者来创建异步代码。

结论

希望以上内容能帮助你了解掌握异步编程的应用场景和工作方式。如果你要计算 Pi 的小数点后 100 万位,那恐怕异步起不了什么作用。

然而,如果你要实现一个服务器,或者其它一些需要大量读写操作的代码,那你肯定能感受到什么叫质的飞跃。这项技术非常强大,一定能让你的程序性能上一个新台阶。英文原文:https://dbader.org/blog/understanding-asynchronous-programming-in-python#.

译者:WDatou

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值