python异步io_Python 异步 IO

本文介绍了如何在Python中使用os.open实现异步文件读写,对比了同步IO和异步IO的区别,并通过实例展示了在处理大量数据时的差异。异步IO在写入大文件时可能只完成部分写入,需要额外的逻辑确保数据完整。最后,讨论了在多线程环境中非阻塞IO的优势。
摘要由CSDN通过智能技术生成

Python 异步 IO

通常用异步 IO 用于服务器端的网络编程,但是磁盘文件的操作也是可以进行异步 IO 的,本文将会介绍怎么用 Python 进入异步读写文件。

本文将使用 os.open 这个低层的 API 来实现异步 IO 的功能。

python-sqlpy.jpg

open 与 os.open 的关系

open 函数是对 os.open 函数的封装,如果想使用异步 IO 直接使用 os.open 就行。

1、用 open 实现向一个文件写入四字节数据。

In [1]: sync_file_path = '/tmp/sync.log'

In [2]: sync_file = open(sync_file_path,'bw')

In [3]: data = b'ABCD'

In [4]: chunck_length = sync_file.write(data) # write 函数的返回值就是完成写入的大小,这里 data 只有 4 字节所以返回 4.

In [5]: print(chunck_length)

4

In [6]:

sync.log 的指纹是。

ll /tmp/sync.log

-rw-r--r-- 1 jianglexing wheel 4 6 4 10:25 /tmp/sync.log

md5 /tmp/sync.log

MD5 (/tmp/sync.log) = cb08ca4a7bb5f9683c19133a84872ca7

2、同样的逻辑用 os.open 来实现。

In [1]: import os

In [2]: data = b'ABCD'

In [3]: async_file_path = '/tmp/async.log'

In [4]: fileno = os.open(async_file_path,os.O_CREAT | os.O_WRONLY)

In [5]: os.write(fileno,data)

Out[5]: 4

async.log 的指纹值。

ll /tmp/async.log

-rwxr-xr-x 1 jianglexing wheel 4 6 4 10:29 /tmp/async.log

md5 /tmp/async.log

MD5 (/tmp/async.log) = cb08ca4a7bb5f9683c19133a84872ca7

同步 IO 与异步 IO 的区别

到目前为止我们都还是只用了同步 IO 的功能,不管是使用 open 还是 os.open,在开始使用异步 IO 之前我们先来看一下异步 IO 与同步 IO 的区别。

1、同步 IO 的情况下 write 还是阻塞的,也就是说要完成所有的写入之后它才会返回,返回值就是写入的字节数;这个字节数和要写入的数据长度是一样长的,原因是它要写完才返回。

2、异步 IO 的情况下 write 是不阻塞的,也就是说 write 会更加快的返回,它不要等到完全把数据写完;这个时候 write 的返回值就不是数据的大小了,而是有多数字节完成了写入。

同步 IO vs 异步 IO

1、采用同步 IO 的方式向文件中写入 4G 数据。

In [1]: sync_file_path = '/tmp/sync.log'

In [2]: data = b'ABCD' * 1024 * 1024 * 1024

In [3]: sync_file = open(sync_file_path,'bw')

In [4]: sync_file.write(data)

Out[4]: 4294967296

In [5]: sync_file.close()

ll /tmp/sync.log

-rw-r--r-- 1 jianglexing wheel 4294967296 6 4 10:56 /tmp/sync.log

md5 /tmp/sync.log

MD5 (/tmp/sync.log) = 6a628b7c7be4251d724217b78b52ef6c

可以看到当 4G 数据都写完之后 write 函数返回。

2、采用异步 IO 的方式向文件中写入 4G 数据。

In [1]: import os

In [2]: async_file_path = '/tmp/async.log'

In [3]: data = b'ABCD' * 1024 * 1024 * 1024

In [4]: async_file = os.open(async_file_path,os.O_WRONLY | os.O_NONBLOCK)

In [5]: os.write(async_file,data)

Out[5]: 2147483647

In [7]: 2147483647 /1024/1024/1024

Out[7]: 1.9999999990686774

可以看到只写了 1.9G 数据就返回了,为了能把数据完整的写完,我们就要加上其它的逻辑来保证。

import os

# 异步写入的目标文件

async_file_path = '/tmp/async.log'

# 4G 数据

data = b'ABCD' * 1024 * 1024 * 1024

def async_write_all(file_path, data):

"""实现一个异步写入所有数据的函数

"""

# 总长度

total_length = len(data)

# 已经完成写入的长度

writed_length = 0

#

print("start write .")

try:

fileno = os.open(file_path, os.O_WRONLY | os.O_NONBLOCK)

# 由于 write 不保证全部写完成,所以要应用程序来保证。

while total_length != writed_length:

# 当两个长度相等后说明写入完成了

writed_length = writed_length + \

os.write(fileno, data[writed_length:])

progress = writed_length / total_length

print(f"writing {progress:.3} .")

except IOError as err:

pass

finally:

os.close(fileno)

print("complete write .")

async_write_all(async_file_path, data)

运行效果。

python3 main.py

start write .

False

writing 0.5 .

writing 1.0 .

writing 1.0 .

complete write .

3、验证两种方式结果是一样的。

-rwxr-xr-x 1 jianglexing wheel 4294967296 6 4 11:26 async.log

-rw-r--r-- 1 jianglexing wheel 4294967296 6 4 10:56 sync.log

md5 async.log

MD5 (async.log) = 6a628b7c7be4251d724217b78b52ef6c

md5 sync.log

MD5 (sync.log) = 6a628b7c7be4251d724217b78b52ef6c

注意

对于读写磁盘上的文件,以 NONBLOCKING 的模式打开它其它并没有什么实际的用途,一来它写的时候还会被卡住,二来他返回之后还要程序自己检测是否已经写完,这都增加了程序的复杂度。

如果是同时要读写多个磁盘文件的话,可以采用多线程技术。前面说非阻塞在写入的时候会卡住是因为我们只有一个线程,现在说多线程有用是因为写的时候会释放 GIL 锁,其它线程还是有执行机会的。

对于 socket 文件来说非阻塞 IO 加事件循环还是有用的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值