像tokio这样的Rust异步运行时提供了许多标准函数的“异步化”副本,包括一些文件IO函数,其原理基本上是通过召唤相应的阻塞任务(在新线程上?)来工作。这些函数的例子包括tokio::fs::create_dir_all
、tokio::fs::read_dir
、tokio::fs::read
等。
所有这些函数的优点是什么?在异步上下文中,为什么我应该更喜欢使用它们而不是标准的阻塞函数?如果我正在await
它们的结果,是否有任何收益?
一个例子是一个基于查询返回某个文件内容的异步Web路由(使用Rocket):
#[get("/foo/<query>")]
async fn foo(query: &str) -> Option<String> {
let file_path = // interpret `query` somehow and find the target file
tokio::fs::read_to_string(file_path).await.ok()
// ^ Why not just `std::fs::read_to_string(file_path).ok()`?
}
我理解在Socket IO或者延时任务(with thread sleep)中使用async/.await
的好处,但是在这种情况下似乎对我来说没有意义。相反的,这使得更加复杂的任务在代码中更难解决(例如,在目录列表中搜索文件时处理流)。
.await
允许它们在执行文件IO的另一个线程被阻塞时继续运行。在异步任务中使用阻塞调用会阻塞该线程,从而阻塞了在同一线程上运行的所有任务。 - Herohtarepoll()
或类似的方法查询文件描述符,这个说法才是正确的,即文件描述符“准备好”并不意味着从中读取数据不会因为需要从磁盘(或网络挂载的文件系统中)读取内容而被阻塞,这可能需要很长时间甚至永远无法完成。通过异步版本的std::fs
操作,可以将阻塞操作转移到单独的线程中,并安排在整个操作完成时进行唤醒,从而解决了这个问题。 - user4815162342