Tokio 新版本发布,现在支持文件系统

2018年5月5日

比我最初希望的要花更长的时间(总是这样),但 Tokio 的新版本已经发布了。此版本除了其他功能外,还包括一组新的 API,允许从异步上下文中执行文件系统操作。

文件系统 API

与文件(和其他文件系统类型)交互需要*阻塞系统调用,我们都知道阻塞和异步不能混合使用。因此,从历史上看,当人们问“如何从文件中读取和写入文件?”时,答案是使用线程池。 想法是,当必须执行阻塞读取或写入时,它在线程池上完成,这样它就不会阻塞异步 reactor。

需要单独的线程池来执行文件操作需要消息传递。 异步任务必须向线程池发送消息,要求它从文件中读取,线程池执行读取并将结果填充到缓冲区中。 然后,线程池将缓冲区发送回异步任务。 这不仅增加了调度消息的开销,而且还需要分配缓冲区来来回发送数据。

现在,借助 Tokio 新的 文件系统 API,不再需要这种消息传递开销。 添加了一个新的 File 类型。 此类型看起来与 std 提供的类型非常相似,但它实现了 AsyncReadAsyncWrite,使其可以安全地直接从 Tokio 运行时上运行的异步任务中使用。

由于 File 类型实现了 AsyncReadAsyncWrite,因此它可以像从 Tokio 使用 TCP 套接字一样使用。

截至今天,文件系统 API 非常精简。 还有许多其他 API 需要实现,以使 Tokio 文件系统 API 与 std 保持一致,但这些都留给读者作为练习提交 PR!

* 是的,有些操作系统提供完全异步的文件系统 API,但这些 API 要么不完整,要么不可移植。

标准输入和输出

此版本的 Tokio 还包括异步 标准输入标准输出 API。 因为很难以可移植的方式提供真正的异步标准输入和输出,所以 Tokio 版本使用了与阻塞文件操作 API 类似的策略。

阻塞

这些新 API 的实现归功于新的 blocking API,它允许注释将阻塞当前线程的代码段。 这些阻塞部分可以包括阻塞系统调用、等待互斥锁或 CPU 密集型计算。

通过告知 Tokio 运行时当前线程将阻塞,运行时能够将事件循环从当前线程移动到另一个线程,从而释放当前线程以允许阻塞。

这与使用消息传递在线程池上运行阻塞操作相反。 不是将阻塞操作移动到另一个线程,而是移动整个事件循环。

实际上,将事件循环移动到另一个线程比移动阻塞操作便宜得多。 这样做只需要几个原子操作。 Tokio 运行时还保留一个备用线程池,以便尽可能快地移动事件循环。

这也意味着使用 blocking 注解和 tokio-fs 必须从 Tokio 运行时的上下文中完成,而不是从其他 futures 感知的执行器中完成。

当前线程运行时

此版本还包括运行时的“当前线程”版本(感谢 kpp)。 这与现有运行时类似,但所有组件都在当前线程上运行。 这允许运行未实现 Send 的 futures。