asyncio 如何安排文件系统统计操作?

How can I asyncio schedule a filesystem stat operation?

尽快将一些代码转换为使用 asyncio, I'd like to give back control to the asyncio.BaseEventLoop。这意味着避免阻塞等待。

如果没有 asyncio,我会使用 os.stat() or pathlib.Path.stat() 来获取例如文件大小。有没有办法用 asyncio 高效地做到这一点?

我可以只包装 stat() 调用,使其成为类似于 described here 的未来吗?

os.stat() 转换为 stat 系统调用:

$ strace python3 -c 'import os; os.stat("/")'
[...]
stat("/", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
[...]

这是阻塞的,无法获得非阻塞 stat 系统调用。

asyncio 通过使用已经存在的非阻塞系统调用提供非阻塞 I/O(参见 man fcntl,其 O_NONBLOCK 标志,或 ioctl), 所以 asyncio 并没有使系统调用异步,它以一种很好的方式公开了已经异步的系统调用。

仍然可以使用漂亮的 ThreadPoolExecutor 抽象来使用线程池并行进行阻塞 stat 调用。

不过你可以先考虑一些其他的参数:

  • 根据 strace -Tstat 很快:stat("/", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 <0.000007>,可能比启动和同步线程要快。
  • stat 在很多情况下可能受 IO 限制,因此使用更多 CPU 无济于事
  • 并行 I/O 可能会破坏随机访问的良好顺序访问,物理硬盘驱动器在这种情况下可能会更慢。

但是使用线程池也有很多可能使您的 stat 更快,就像您正在访问分布式文件系统一样。

您也可以看看 functools.lru_cache:如果您在同一个文件或目录上执行多个 stat,并且您确定它没有改变,缓存结果可以避免一个系统调用。

总而言之,"keep it simple"、"os.stat" 获取文件大小的有效方法。