Python中使用asyncio 封装文件读写


Posted in Python onSeptember 11, 2016

前言

和网络 IO 一样,文件读写同样是一个费事的操作。

默认情况下,Python 使用的是系统的阻塞读写。这意味着在 asyncio 中如果调用了

f = file('xx')
f.read()

会阻塞事件循环。

本篇简述如何用 asyncio.Future 对象来封装文件的异步读写。

代码在 GitHub。目前仅支持 Linux。

阻塞和非阻塞

首先需要将文件的读写改为非阻塞的形式。在非阻塞情况下,每次调用 read 都会立即返回,如果返回值为空,则意味着文件操作还未完成,反之则是读取的文件内容。

阻塞和非阻塞的切换与操作系统有关,所以本篇暂时只写了 Linux 版本。如果有过 Unix 系统编程经验,会发现 Python 的操作是类似的。

flag = fcntl.fcntl(self.fd, fcntl.F_GETFL) 
if fcntl.fcntl(self.fd, fcntl.F_SETFL, flag | os.O_NONBLOCK) != 0: 
  raise OSError()

Future 对象

Future 对象类似 Javascript 中的 Promise 对象。它是一个占位符,其值会在将来被计算出来。我们可以使用

result = await future

在 future 得到值之后返回。而使用

future.set_result(xxx)

就可以设置 future 的值,也意味着 future 可以被返回了。await 操作符会自动调用 future.result() 来得到值。

loop.call_soon

通过 loop.call_soon 方法可以将一个函数插入到事件循环中。

至此,我们的异步文件读写思路也就出来了。通过 loop.call_soon 调用非阻塞读写文件的函数。若一次文件读写没有完成,则计算剩余所学读写的字节数,并再次插入事件循环直至读写完毕。

可以发现其就是把传统 Unix 编程里,非阻塞文件读写的 while 循环换成了 asyncio 的事件循环。

下面是这一过程的示意代码。

def read_step(self, future, n, total):
  res = self.fd.read(n)
  if res is None:
    self.loop.call_soon(self.read_step, future, n, total)
    return
  if not res: # EOF
    future.set_result(bytes(self.rbuffer))
    return
  self.rbuffer.extend(res)
  self.loop.call_soon(self.read_step, future, self.BLOCK_SIZE, total)

def read(self, n=-1):
  future = asyncio.Future(loop=self.loop)

  self.rbuffer.clear()
  self.loop.call_soon(self.read_step, future, min(self.BLOCK_SIZE, n), n)

  return future
Python 相关文章推荐
python获取糗百图片代码实例
Dec 18 Python
go和python调用其它程序并得到程序输出
Feb 10 Python
python根据文件大小打log日志
Oct 09 Python
详解Python中for循环的使用
Apr 14 Python
用Python的Django框架来制作一个RSS阅读器
Jul 22 Python
Django使用Celery异步任务队列的使用
Mar 13 Python
Python代码打开本地.mp4格式文件的方法
Jan 03 Python
Python实现DDos攻击实例详解
Feb 02 Python
解决.ui文件生成的.py文件运行不出现界面的方法
Jun 19 Python
Python读取xlsx文件的实现方法
Jul 04 Python
pyqt5中动画的使用详解
Apr 01 Python
python 在右键菜单中加入复制目标文件的有效存放路径(单斜杠或者双反斜杠)
Apr 08 Python
Python 如何访问外围作用域中的变量
Sep 11 #Python
Python优化技巧之利用ctypes提高执行速度
Sep 11 #Python
Python 中的with关键字使用详解
Sep 11 #Python
Python冒泡排序注意要点实例详解
Sep 09 #Python
通过5个知识点轻松搞定Python的作用域
Sep 09 #Python
python验证码识别的实例详解
Sep 09 #Python
Python随机数random模块使用指南
Sep 09 #Python
You might like
基于ubuntu下nginx+php+mysql安装配置的具体操作步骤
2013/04/28 PHP
小谈php正则提取图片地址
2014/03/27 PHP
php使用timthumb生成缩略图的方法
2016/01/22 PHP
js wmp操作代码小结(音乐连播功能)
2008/11/08 Javascript
jquery的键盘事件修改代码
2011/02/24 Javascript
NodeJS 模块开发及发布详解分享
2012/03/07 NodeJs
基于jquery自定义的漂亮单选按钮RadioButton
2013/11/19 Javascript
SeaJS入门教程系列之SeaJS介绍(一)
2014/03/03 Javascript
JavaScript之Object类型介绍
2015/04/01 Javascript
ES6新特性四:变量的解构赋值实例
2017/04/21 Javascript
vue页面使用阿里oss上传功能的实例(一)
2017/08/09 Javascript
jQuery实现合并表格单元格中相同行操作示例
2019/01/28 jQuery
基于Vue的商品主图放大镜方案详解
2019/09/19 Javascript
JS中准确判断变量类型的方法
2020/06/01 Javascript
vue 解决IOS10低版本白屏的问题
2020/11/17 Javascript
[01:04:05]VG vs Newbee 2018国际邀请赛小组赛BO2 第一场 8.17
2018/08/20 DOTA
[53:52]OG vs EG 2018国际邀请赛淘汰赛BO3 第二场 8.23
2018/08/24 DOTA
python改变日志(logging)存放位置的示例
2014/03/27 Python
Python 中 Meta Classes详解
2016/02/13 Python
python判断一个数是否能被另一个整数整除的实例
2018/12/12 Python
使用PIL(Python-Imaging)反转图像的颜色方法
2019/01/24 Python
Python Flask框架扩展操作示例
2019/05/03 Python
如何安装并使用conda指令管理python环境
2019/07/10 Python
一篇文章搞定Python操作文件与目录
2019/08/13 Python
如何基于python操作excel并获取内容
2019/12/24 Python
巧用HTML5给按钮背景设计不同的动画简单实例
2016/08/09 HTML / CSS
加拿大休闲和工业服装和鞋类零售商:L’Équipeur
2018/01/12 全球购物
澳大利亚优惠网站:Deals.com.au
2019/07/02 全球购物
新娘父亲婚礼致辞
2014/01/16 职场文书
幼儿园教师请假制度
2014/01/16 职场文书
校庆活动方案
2014/03/31 职场文书
拉歌口号大全
2014/06/13 职场文书
写给媳妇的检讨书
2015/05/06 职场文书
小学体育组工作总结
2015/08/13 职场文书
安全生产培训心得体会
2016/01/18 职场文书
蓝天保卫战收官在即 :15行业将开展环保分级评价
2019/07/19 职场文书