Tokio の非同期コンテキストでファイルの読み書きを行う際によく使われるモジュールは tokio::fs
で、標準ライブラリのファイル API の同名の非同期バージョンが含まれています。
ほとんどのファイルメソッドは標準ライブラリの同名メソッドと同じ意味を持っていますが、flush
メソッドは少し異なります。
tokio::fs
ドキュメントの開始#
この記事を書いている時点で、Tokio の最新バージョンは 1.43.0 です。ドキュメントリンクをクリックすると、このモジュールの簡単な紹介が見られます:
ドキュメントによれば、ほとんどのオペレーティングシステムが非同期のファイルシステム API を提供していないため、tokio::fs
の非同期ファイル操作は実際には一般的な同期ファイル操作を spawn_blocking
のスレッドプールで実行することになります。
将来的には io_uring
のような非同期ファイルシステム API に切り替える可能性がありますが、少なくとも現在はすべてのプラットフォームでスレッドプールを使用して実装されています。
ドキュメントの flush メソッドに関する特別な説明#
少し下にスクロールすると、次のような説明が見られます:
ドキュメントは特に Note
を使用して、Tokio の File
に書き込む際に flush
を使用することが重要であるとユーザーに警告しています。なぜなら、write
メソッドの呼び出しは書き込みが完了する前に戻り、明示的に flush
を呼び出さなければ書き込みが完了するのを待たないからです。
これは標準ライブラリの File
とは異なります。これは spawn_blocking
の基盤となる実装によるものです。
標準ライブラリの flush
#
なぜ Tokio はユーザーに flush
を呼び出すように特に注意を促すのでしょうか?標準ライブラリには flush
がないのか、または flush
を呼び出す必要がないのでしょうか?これについては標準ライブラリのドキュメントを見てみる必要があります。
本当にそうです! File
構造体にはバッファが含まれていないため、Unix
および Windows
上の flush
メソッドは何の操作も行いません。言い換えれば、99.99% のプラットフォームで標準ライブラリの File
を使用して書き込みを行い、flush
を忘れてもプログラムの論理には影響しません。
もちろん、ドキュメントには補足があり、これは理解を助けるために提供された基盤の説明であり、拘束力はなく、今後のバージョンの動作も変更される可能性があることが示されています。
補足説明#
調査中に、Tokio の主要な著者である alice がフォーラムでの返信を見つけました。これが理解を助けるかもしれません。
tokio::fs::File での書き込みが完了したということは、それがスレッドプールに渡されて書き込まれることを意味しますが、それがスレッドプールによってまだ書き込まれたことを意味するわけではありません。flush を呼び出すことで、それが起こるのを待つことができます。
これは変更することはできません。AsyncWrite トレイトのシグネチャが根本的に私たちにこの方法で処理することを強制しています。
tokio::fs::File
への書き込み操作が完了したということは、それがスレッドプールに渡されて書き込まれることを意味しますが、それがスレッドプールによって書き込まれたことを意味するわけではありません。flush
を呼び出すことで、書き込み操作が実際に完了するのを待つことができます。
これは変更できません。AsyncWrite
トレイトのシグネチャが根本的に私たちにこの方法で処理することを強制しています。
まとめ#
細部を省いて、私たちはシンプルな結論を導き出すことができます:
ファイルに書き込む際に flush
をしないことは間違いがあるかもしれませんが、flush
をすることは確実に間違いではありません!
ファイルを書いた後にバッファをフラッシュすることは良い習慣であり、効果がないかもしれませんが、問題が発生することはありません。😋