Develop with asyncio部分的翻译
Develop with asyncio
异步程序和普通的连续程序(也就是同步程序)是很不一样的,这里会列出一些常见的陷阱,并介绍如何去避开他们。
Debug mode of asyncio
我们用asyncio就是为了提高性能,而为了更容易去开发编写异步的代码,我们需要开启debug模式
在应用中开启调试模式:
- 全局开启异步的调试模式,可以通过设置环境变量
PYTHONASYNCIODEBUG=1,或者直接调用AbstractEventLoop.set_debug() - 设置
asynico logger的日志等级为DEBUG,如在代码开头logging.basicConfig(level=logging.DEBUG) - 配置
warnings模块去显示ResourceWarning警告,如在命令行中添加-Wdefault这个选项去启动python来显示这些
Cancellation
取消任务(执行)这个操作对于普通的程序来讲并不常见,但是在异步程序中,这不仅是个很普通的事情,而且我们还需要去准备好去处理它们。
可以直接用Future.cancel()这个方法去取消掉Future类和tasks类,而wait_for这个方法则是直接在超时的时候取消掉这些任务。以下是一些间接取消任务的情况
如果在Future被取消之后调用它的set_result()或者set_exception(),它将会被一个异常导致执行失败(后半句原句为: it would fail with an exception)
if not fut.cancelled():
fut.set_result('done')
不要直接通过AbstractEventLoop.call_soon()去安排future类调用set_result()或者set_exception(),因为这个future类可以在调用这些方法前被取消掉
如果你等待一个future类的返回,那么就应该提早检查这个future类是否被取消了,这样可以避免无用的操作
@coroutine
def slow_operation(fut):
if fut.cancelled():
return
# ... slow computtaion ...
yield from fut
# ...
shield()方法能够用来忽略取消操作
Concurrency and multithreading
一个事件循环是跑在一个线程上面的,它所有的回溯函数和任务也是执行在这个线程上的。当事件循环里面跑着一个任务的时候,不会有其他任务同时跑在同一个线程里,但是当这个任务用了yield from之后,那么这个任务将会被挂起,并而事件循环会去执行下一个任务。
在不是同一线程的时候,应该用AbstractEventLoop.call_soon_threadsafe()方法来安排回溯。
loop.call_soon_threadsafe(callback, *args)
大多数asyncio对象都不是线程安全的。所以需要注意的是是否有在事件循环之外调用这些对象。举个栗子,要取消一个future类的时候,不应该直接调用Future.cancel(),而是loop.call_soon_threadsafe(fut.cancel)
为了控制信号和执行子进程,事件循环必须运行在主线程。
在不同线程安排回溯对象时,应该用run_coroutine_threadsafe(),它会返回一个concurrent.futures.Future对象去拿到结果
future = asyncio.run_coroutine_threadsafe(coro_func(), loop)
result = future.result(timeout) # wait for the result with a timeout
AbstractEventLoop.run_in_executor()能够用来作为线程池的执行者,在不同的线程里去执行回溯,而且不会阻塞当前线程的事件循环。
Handle blocking functions correctly
阻塞函数不应该被直接调用。举个栗子,如果一个函数被阻塞了一秒,那么其他任务都会被延迟一秒,这会产生很大的影响。
在网络和子进程方面,asynico提供了高级的API如协议。
AbstractEventLoop.run_in_executor()方法能够在不阻塞当前线程的事件循环的前提下,去调用其他线程或者进程的任务。
Logging
asyncio模块的日志信息在logging模块的asyncio实例里
默认的日志等级是info,可以根据自己的需要改变
logging.getLogger('asyncio').setLevel(logging.WARNING)
Detect coroutine objects never scheduled
当一个协程函数被调用,然后如果它的返回值没有传给ensure_future()或者AbstractEvenLoop.create_task()
的话,执行这个操作的协程对象将永远不会被安排执行,这可能就是一个bug,可以开启调试模式去通过warning信息找到它。
举个栗子
import asyncio
@asyncio.coroutine
def test():
print('never scheduled')
test()
在调试模式下会输出
Coroutine test() at test.py:3 was never yielded from
Coroutine object create at (most recent call last):
File 'test.py', line 7, in <module>
test()
解决方法就是去调用ensure_future()函数或者通过协程对象去调用AbstractEventLoop.create_task()
Detect exceptions never consumed
python经常会调用sys.displayhook()来处理那些未经处理过的异常。如果Future.set_exception()被调用,那么这个异常将不会被消耗掉(处理掉),sys.displayhook()也不会被调用。取而代之的是,当这个future类被垃圾回收机制删除的时候,日志将会输出这个异常的相关错误信息。
举个栗子,unhandled exception
import asyncio
@asyncio.coroutine
def bug():
raise Exception('not consumed')
loop = asyncio.get_event_loop()
asyncio.ensure_future(bug())
loop.run_forever()
loop.close()
输出将是
Task exception was never retrieved
future: <Task finished coro=<coro() done, defined at asyncio/coroutines.py:139> exception=Exception('not consumed',)>
Traceback (most recent call last):
File "asyncio/tasks.py", line 237, in _step
result = next(coro)
File "asyncio/coroutines.py", line 141, in coro
res = func(*args, **kw)
File "test.py", line 5, in bug
raise Exception("not consumed")
Exception: not consumed
开启asyncio的调试模式后,会得到具体位置的错误信息
Task exception was never retrieved
future: <Task finished coro=<bug() done, defined at test.py:3> exception=Exception('not consumed',) created at test.py:8>
source_traceback: Object created at (most recent call last):
File "test.py", line 8, in <module>
asyncio.ensure_future(bug())
Traceback (most recent call last):
File "asyncio/tasks.py", line 237, in _step
result = next(coro)
File "asyncio/coroutines.py", line 79, in __next__
return next(self.gen)
File "asyncio/coroutines.py", line 141, in coro
res = func(*args, **kw)
File "test.py", line 5, in bug
raise Exception("not consumed")
Exception: not consumed
可以看到第二行那里,指出了抛出异常的位置
以下提供了几个方法来解决这个问题。第一个方法就是将这个协程链到另外一个协程并使用try/except去捕捉
@asyncio.coroutine
def handle_exception():
try:
yield from bug()
except Exception:
print("exception consumed")
loop = asyncio.get_event_loop()
asyncio.ensure_future(handle_exception())
loop.run_forever()
loop.close()
第二个方法就是用AbstractEventLoop.run_until_complete()
task = asyncio.ensure_future(bug())
try:
loop.run_until_complete(task)
except Exception:
print("exception consumed")
Chain corotuines correctly
当一个协程函数被另外一个协程函数(任务)调用,他们之间应该明确地用yield from 链接起来,不然的话,执行顺序不一定和预想一致。
举个栗子,用asyncio.sleep()模仿的慢操作导致的bug
import asyncio
@asyncio.coroutine
def create():
yield from asyncio.sleep(3.0)
print('(1) create file')
@asyncio.coroutine
def write():
yield from asyncio.sleep(1.0)
print('(2) write into file')
@asyncio.coroutine
def close():
print('(3) close file')
@asyncio.coroutine
def test():
asyncio.ensure_future(create())
asyncio.ensure_future(write())
asyncio.ensure_future(close())
yield from asyncio.sleep(2.0)
loop.stop()
loop = asyncio.get_event_loop()
asyncio.ensure_future(test())
loop.run_forever()
print('Pending tasks at exit: %s' % asyncio.Task.all_tasks(loop))
loop.close()
预想的输出为
(1) create file
(2) write into file
(3) close file
Pending tasks at exit: set()
实际的输出为
(3) close file
(2) write into file
Pending tasks at exit: {<Task pending create() at test.py:7 wait_for=<Future pending cb=[Task._wakeup()]>>}
Task was destroyed but it is pending!
task: <Task pending create() done at test.py:5 wait_for=<Future pending cb=[Task._wakeup()]>>
事件循环在create()完成前就已经停止掉了,而且close()在write()之前被调用,然而我们所希望调用这些函数的顺序为create(),write(),close()
为了解决这个问题,必须要用yield from来处理这些任务
@asyncio.corotine
def test():
yield from asyncio.ensure_future(create())
yield from asyncio.ensure_future(write())
yield from asyncio.ensure_future(close())
yield from asyncio.sleep(2.0)
loop.stop()
或者可以不要asyncio.ensure_future()
@asyncio.coroutine
def test():
yield from create()
yield from write()
yield from close()
yield from asyncio.sleep(2.0)
loop.stop()
Pending task destroyed
如果挂起的任务被摧毁掉的话,那么包裹它的协程的执行操作将不会完成。这很有可能就是个bug,所以会被warning级别日志记录到
举个栗子,相关的日志
Task was destroyed but it is pending!
task: <Task pending coro=<kill_me() done, defined at test.py:5> wait_for=<Future pending cb=[Task._wakeup()]>>
开启asyncio的调试模式就可以拿到在任务被创建出来的具体位置的错误信息,开启调试模式后的相关日志
Task was destroyed but it is pending!
source_traceback: Object created at (most recent call last):
File "test.py", line 15, in <module>
task = asyncio.ensure_future(coro, loop=loop)
task: <Task pending coro=<kill_me() done, defined at test.py:5> wait_for=<Future pending cb=[Task._wakeup()] created at test.py:7> created at test.py:15>
Close transports and event loops
当一个传输(交互)不再需要的时候,调用它自身的close()方法来释放内存,事件循环也必须明确地关闭掉(也就是要调用事件循环自身的close())
如果transport或者event loop没有被显示地关闭掉,ResourceWarning的警告信息会传给负责摧毁它的那个执行构件。默认情况下,ResourceWarning的警告信息会被忽略掉。
Develop with asyncio部分的翻译的更多相关文章
- Asyncio中Lock部分的翻译
Asyncio中Lock部分的翻译 Locks class asyncio.Lock(*, loop=None) 原始锁的对象. 这个基础的锁是一个同步化的组件,当它上锁的时候就不属于典型的协程了(译 ...
- MVC学习系列14--Bundling And Minification【捆绑和压缩】--翻译国外大牛的文章
这个系列是,基础学习系列的最后一部分,这里,我打算翻译一篇国外的技术文章结束这个基础部分的学习:后面打算继续写深入学习MVC系列的文章,之所以要写博客,我个人觉得,做技术的,首先得要懂得分享,说不定你 ...
- DOTA 2 Match History WebAPI(翻译)
关于DOTA 2 Match History WebAPI 的 源网页地址: http://dev.dota2.com/showthread.php?t=47115 由于源网页全英文,这边做下翻译方便 ...
- 翻译:打造基于Sublime Text 3的全能python开发环境
原文地址:https://realpython.com/blog/python/setting-up-sublime-text-3-for-full-stack-python-development/ ...
- Python PEP 492 中文翻译——协程与async/await语法
原文标题:PEP 0492 -- Coroutines with async and await syntax 原文链接:https://www.python.org/dev/peps/pep-049 ...
- 【译】深入理解python3.4中Asyncio库与Node.js的异步IO机制
转载自http://xidui.github.io/2015/10/29/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3python3-4-Asyncio%E5%BA%93% ...
- 【翻译】Asp.net Core介绍
ASP.NET Core is a significant redesign of ASP.NET. This topic introduces the new concepts in ASP.NET ...
- 为开源社区尽一份力,翻译RocketMQ官方文档
正如在上一篇文章中写道:"据我所知,现在RocketMQ还没有中文文档.我打算自己试着在github上开一个项目,自行翻译."我这几天抽空翻译了文档的前3个小节,发现翻译真的不是一 ...
- asyncio异步IO——Streams详解
前言 本文翻译自python3.7官方文档--asyncio-stream,译者马鸣谦,邮箱 1612557569@qq.com.转载请注明出处. 数据流(Streams) 数据流(Streams)是 ...
随机推荐
- dedecms列表页调用文章正文内容的方法
谁说dede:list 标签不能调用body内容,现在就告诉你,直接就可以调用 第一步,打开后台 核心-->频道模型-->内容模型管理-->普通文章,在列表附加字段中添加body. ...
- vue-cli构建项目使用 less
在vue-cli中构建的项目是可以使用less的,但是查看package.json可以发现,并没有less相关的插件,所以我们需要自行安装. 第一步:安装 npm install less less- ...
- ASP.NET Web API queryString访问的一点总结
自从开始使用ASP.NET Web API,各种路由的蛋疼问题一直困扰着我,相信大家也都一样. Web API的路由配置与ASP.MVC类似,在App_Start文件夹下,有一个WebApiConfi ...
- 20155216 2016-2017-2 《Java程序设计》第八周学习总结
20155216 2016-2017-2 <Java程序设计>第八周学习总结 教材学习内容总结 认识NIO Java NIO 由以下几个核心部分组成: Channels Buffers S ...
- UVALive 7456 Least Crucial Node
题目链接 题意: 给定一个无向图,一个汇集点,问哪一个点是最关键的,如果有多个关键点,输出序号最小的那个. 因为数据量比较小,所以暴力搜索就行,每去掉一个点,寻找和汇集点相连的还剩几个点,以此确定哪个 ...
- Memcached命令:简单获取缓存value用法
Memcached:命令用法1.cmd 输入telnet ip 端口 进入memcached服务端窗口比如:xxx.Token_T1435622096xxx为key获取此key的memcached ...
- js自定制周期函数
function mySetInterval(fn, milliSec,count){ function interval(){ if(typeof count==='undefined'||coun ...
- mysql学习------二进制日志管理
MySQL二进制日志(Binary Log) a.它包含的内容及作用如下: 包含了所有更新了数据或者已经潜在更新了数据(比如没有匹配任何行的一个DELETE) 包含关于每个更新数据库( ...
- scrapy主动触发关闭爬虫
在spider中时在方法里直接写 self.crawler.engine.close_spider(self, 'cookie失效关闭爬虫') 在pipeline和downloaderMiddle ...
- Android 5.0 行为变更
Android 5.0 除了提供诸多新特性和功能外,还对系统和 API 行为做出了各种变更.本文重点介绍您应该了解并在开发应用时加以考虑的一些主要变更. 如果您之前发布过 Android 应用,请注意 ...