异步文件IO有什么意义吗?

Hop*_*ope 9 concurrency asynchronous rust async-await rust-tokio

像tokio这样的 Rust 异步运行时提供了许多标准函数的“异步”副本,包括一些文件 IO 函数,它们基本上只是通过调用相应的阻塞任务(在新线程上?)来工作。此类函数的示例有tokio::fs::create_dir_all, tokio::fs::read_dir, tokio::fs::read, ...

\n

所有这些功能有什么优点?为什么我应该更喜欢在异步上下文中使用它们而不是标准阻塞函数?如果我是.await他们的结果,有什么收获吗?

\n

一个示例是异步 Web 路由,它根据查询返回某个文件的内容(使用Rocket):

\n
#[get("/foo/<query>")]\nasync fn foo(query: &str) -> Option<String> {\n    let file_path = // interpret `query` somehow and find the target file\n    tokio::fs::read_to_string(file_path).await.ok()\n    // ^ Why not just `std::fs::read_to_string(file_path).ok()`?\n}\n
Run Code Online (Sandbox Code Playgroud)\n

async/.await我理解套接字 IO 或延迟任务(线程睡眠)的好处,但在这种情况下,这对我来说似乎毫无意义。但相反的 \xe2\x80\x94 这使得在代码中解决更复杂的任务变得更加困难(例如,在目录列表中搜索文件时使用流)。

\n

Ali*_*yhl 17

tokio::fs::read_to_string和之间的区别std::fs::read_to_string在于 Tokio 函数会将文件 IO 调用卸载到spawn_blocking线程池,而std::fs::read_to_string调用则不会这样做。

这很重要,因为如果您不将文件 IO 卸载到单独的线程,那么您就会阻止运行时取得进展,这意味着运行时中的其他任务将无法在文件 IO 操作期间执行。请参阅链接以获取更多深入的解释。


Why*_*ugo 3

我猜您正在使用相当快的驱动器读取本地文件系统上的小文件。如果是这样的话,那么使用async这些函数的版本可能就没有什么意义了。

如果一半的 HTTP 请求需要从文件系统读取,那么您可能会开始注意到运行时等待阻塞 IO 的时间较长。这实际上取决于您的应用程序的性质。也许你有一个线程?也许你有很多?

然而,在一些极端情况下,文件系统可能会慢到成为一个真正的大问题。这是两个极端的极端情况:

  • 网络安装的文件系统(例如:NFS、ipfs)。下可以有多个网络往返create_dir_all。虽然这是阻塞的,但您的服务基本上没有响应。
  • 硬盘速度慢。旋转的磁盘。或者甚至从 CD-ROM 驱动器中读取。当然,您不会从 CD-ROM 运行您的 Web 服务器,但是如果某些底层库正在执行阻塞 IO,则比较两个磁带(是的,物理磁带仍用于备份)是否相同的工具将受到很大影响。

现在,如果您正在编写一个公开asyncAPI 的库,则不能对底层文件系统或其支持硬件做出假设,并且应该使用非阻塞 IO。