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)是 ...
随机推荐
- 从ACM会议分析我国计算机科学近十年发展情况
从ACM会议分析我国计算机科学近十年发展情况 来源:<中国计算机学会通讯>2015年第10期<专栏> 作者:陈 钢 2006年,承蒙李国杰院士推荐,<中国计算机学会通讯& ...
- # 20155337 2016-2017-2 《Java程序设计》第六周学习总结
20155337 2016-2017-2 <Java程序设计>第六周学习总结 教材学习内容总结 •串流(Stream): 数据有来源及目的地,衔接两者的是串流对象.如果要将数据从来源取出, ...
- CSUST 1506 ZZ的计算器 模拟题
题目描述:实现一个计算器,可以进行任意步的整数以内的加减乘除运算,运算符号只有+.-.*./,求出结果. 解题报告:一个可以说麻烦的模拟题,我们可以这样,输入以字符串的形式输入,然后将输入先做一遍预处 ...
- 用代码截图去理解MVC原理
[概述] 看了蒋金楠先生的<Asp.Net Mvc框架揭密>,这本书详细地讲解了mvc的原理,很深奥也很复杂,看了几遍才将就明白了一点.他在第一章用了一个他自己写的mvc框架作为例子,代码 ...
- 内核早期内存分配器:memblock
内核早期内存分配器:memblockLinux内核使用伙伴系统管理内存,那么在伙伴系统工作前,如何管理内存?答案是memblock.memblock在系统启动阶段进行简单的内存管理,记录物理内存的使用 ...
- 在windows上实现多个java jdk的共存解决办法
转自:https://www.cnblogs.com/jianyungsun/p/6918024.html 分析问题 为了多快好省的解决当前的问题,我的想法是在windows中同时安装jdk1.6和j ...
- Visual Studio 2013更新内容简介
前言 VS2013终于发布了,虽然之前自己使用VS2010和VS2012的时间也不长,尤其是VS2012这自己刚刚也没用多久,看到VS2013发布了,自己忍不住也下载了下来,官网肯定可以下载,不过自己 ...
- 关于RestFul API 介绍与实践
之前演示的PPT,直接看图... •参考链接: •RESTful API 设计最佳实践 •RESTful API 设计指南 •SOAPwebserivce和RESTfulwebservice对 ...
- 数据库SQL中case when函数的用法
Case具有两种格式,简单Case函数和Case搜索函数.这两种方式,可以实现相同的功能.简单Case函数的写法相对比较简洁,但是和Case搜索函数相比,功能方面会有些限制,比如写判断式. 简单Cas ...
- 字符串格式化格式 -- Numeric Format Strings