ネタ元:[OpenMP]分身!
WriteFile/ReadFile は同期I/Oと、非同期I/Oの2つのI/Oモードに対応したAPIです。I/Oの同期非同期と、スレッド間同期は異なる概念のものなので混同してはいけません。
ということで、ネタ元でどうしても納得できない部分がここ。
あっと、WriteConsole 関数のリファレンスに「WriteFile 関数に似た動作をします 」と書いてある WriteFile 関数のリファレンスには、「この関数は、同期と非同期両方の操作を想定して設計されています。 」と、書かれています。出力をバッファリングして、対象のストリームへ非同期に処理するようです。ということは、「スレッド セーフな関数を使って表示する。 Print the parameter values using thread-safe functions. 」というコメントが、ヘン?この「using thread-safe functions.」というのは、「非同期処理」の意かと思われます。 |
リファレンスから引用した部分を文章として完結する形で改めて引っ張りなおしてみます(「」の部分です)。
WriteConsole のリファレンスは解説部分の「WriteConsole 関数は、コンソールスクリーンバッファに文字を書き込みます。WriteFile 関数に似た動作をしますが、WriteConsole 関数は Unicode( ワイド文字)と ANSI のどちらのモードでも書き込みが可能です。」を引用したと思われます。
WriteFile のリファレンスのほうは、冒頭部分「ファイルにデータを書き込みます。この関数は、同期と非同期両方の操作を想定して設計されています。ファイルポインタの現在の位置が、書き込みの開始位置になります。書き込みが完了すると、ファイルポインタの位置は、実際に書き込んだバイト数だけ進みます。FILE_FLAG_OVERLAPPED を指定してファイルを開いた場合は、この限りではありません。オーバーラップ I/O(非同期 I/O)を指定してファイルのハンドルを作成した場合は、アプリケーション側でファイルポインタを調整してください。」だと思われます。
改めて書きます。
WriteFile の冒頭にある同期、非同期は I/O の同期、非同期であって、マルチスレッドプログラムにおける同期化処理(同期制御)とは異なるものです。したがって(おそらくはPOSIX文書のコメントと思われる)、「スレッドセーフな...」という部分を当てはめることはできないのではないか?と思われます。
せっかくなので、同期I/Oと非同期I/Oについて。
本当は、Blogでのつたない説明よりも、「Win32マルチスレッドプログラミング」(ISBN4-7561-1404-0:絶賛絶版中)という書籍を読んでもらったほうが、ずっとわかりやすいし充実した内容なのですが、いかんせん絶版になって久しい書籍だし、おそらく一刷しかしてないと思うので、現在では入手そのものがほぼ不可能なのではないか?と思われます。ちなみに、出版はAddison-Wesleyです。
ま、ないものをねだっても仕方がないので、大雑把ですが、簡単な解説を。
最初に書いたように、Windowsのファイル操作には、同期I/Oと呼ばれるものと非同期I/Oと呼ばれるものの2種類のI/Oモードがあります。同期I/Oは、要求した処理をその場で処理するモードで、APIを呼び出したら、そのAPIから帰ってきた時点で実際の処理も終了しているというものです。多くのプログラムのファイル操作がこの同期I/Oを用いています。
これに対し、非同期I/Oは要求した処理をその場では行わず、処理の予約だけを行い、API呼び出し時点ではない別の適切なタイミングを見計らって実際のファイル操作を行うモードとなります。そのため、APIから帰ってきた時点では処理が終わっていないため、対象となるバッファや追加情報などを処理終了まで保持しておかなければならないという制約があります。
Windows ではこの非同期I/Oに2種類のモードを用意しています。一つは使い方が比較的単純な Overlapped I/O と呼ばれるもので、処理の終了通知にHANDLEか、手動イベントのいずれかを使って通知する(どちらを使うかはOVERLAPPED構造体の内容次第)仕組みで動作します。こちらは、いわゆる同期オブジェクトを用いた処理となるため、MsgWaitForMultipleObjects などを用いてメインスレッド側で適宜処理を行うなどが可能になっているため、比較的創りやすい仕組みになっています。
もう一つはAPC(非同期プロシージャコール:Asyncronous Procedure Callの略)と呼ばれる、非同期の終了通知関数を使った処理となります。こちらは、ReadFileEx/WriteFileEx というEx付きのAPIを使います。ほぼ同じ形で.NET Framework の FileStream にも実装されています(BeginRead/BeginWrite)ので、利用したことがある人もいるかもしれません。
昨今話題の並列処理、あるいは同時実行処理(並行処理)のボトルネックと呼ばれるものにファイルアクセスがあります。ですがこれにも非同期処理モードがあります。うまく活用すれば、シーケンシャルではあるものの複数の処理を分散させて行うことができるなどうまく活用することができる場面もあるかもしれません。
モバイル系OSと共有しているフレームワーク的なものの場合、利用は難しいかもしれませんが、デスクトップアプリあるいはサーバーアプリだけなら、利用を制限するようなOS(9x系OS)はすでに駆逐されたに等しい状況です。積極的に活用しても怒られない時代になったといえるでしょう。
まぁどうでもいい話題ですが、ファイルアクセスの非同期処理は、別にWindowsNTだからではなく、MS-DOSや、PC-8801mkIIなんかの時代にもあったものです。誰もが使うとか簡単に使えるというものではありませんでしたが、高速化のテクニックとしては30年近く前のPCゲームですでに使われていたものだということだけ付け加えておきます。