著作一覧 |
yamazさんによるread/writeで待ち状態にならずにIOを発行することを何気なく非同期IOと呼ぶけれど、プログラミングモデルとしての非同期IOJ(async io)とselectを使ったマルチプレキシングは違うという話。先日僕がささださんとRailsChatでこのあたりについて聞いた話とか、そのあたりがからんできているのだと思う。
で、読んでいてはっと思いだしたのは、Win32APIの非同期(overlappedの略語としてMSDNでは非同期を当てている)は、これとは異なる意味だということだ。(.NET Frameworkでは、yamazさんが書いている意味になる)
オーバーラップ I/O(非同期 I/O)を指定してファイルのハンドルを作成した場合は、読み取り操作が完了した後、アプリケーション側でファイルポインタを調整してください。(ReadFile APIの説明から)
(英語版とは現在内容がちょっと変わってきている。英語版にはこの文に相当するのはないが、
You can use this function for both synchronous and asynchronous operation.(ReadFile [Files])とasynchronous operationという言葉は利用している)
Windowsでは、ノンブロッキングと呼ばずに、オーバーラップと呼ぶのだが、具体的にはCreateFile APIでファイルハンドルを作成するときにFILE_FLAG_OVERLAPPED を指定したものは、当然オーバーラップIOの対象となり訳語からは非同期IOということになる。ハンドルのオープン時点にどのようなIOを行うかが決定されるということである。
FILE_FLAG_OVELAPPEDを指定したハンドルに対して利用できるAPIは、ReadFileとReadFileEx(とWriteFile、WriteFileExだが、以後はReadFile*だけに絞る)の両方だが、どちらを使うかでプログラミングモデルが変わることになる。でも、どちらもオーバーラップ指定したハンドルに対するIOであり、オーバーラップに非同期を訳語として割り当ててあるため、どちらにしても非同期IOとなるわけだ。
実際、ReadFileEx(このAPIは、aioのようにコールバックルーチンを指定する)の説明には
ReadFile 関数は同期と非同期の両方の操作を想定して設計されていますが、ReadFileEx 関数は非同期の操作だけを想定して設計されています。
のように、ReadFileでも非同期なIOを利用できるという書き方になる。しかしReadFileの場合、コールバックルーチンは指定できず、hEventというイベントハンドルを指定する。プログラムは、WaitForMultipleObjects APIなどにこのイベントハンドルを与えてシグナル状態かどうかでIO完了を判定する。(Win32には、POSIXの意味でのシグナルは存在しないので――バギーだと評されるエミュレートはないわけではないけど――シグナルというのは、モニターのロック状態を意味するというか、要するにWin32のシグナルなんだけど)
このReadFileによる非同期IO処理(ということにWin32のAPIの定義からは当然なる)の意味は、yamazさんのところでselect(2)について説明しているIO多重化と同じプログラミングモデルだ。
僕が、selectやpollを使ったIOについても非同期と呼ぶのは、Windowsプログラマとしての観点からだったのだな、と思い出した次第。
#あと、POSIXのselectでは、最初にターミネーションしたディスクリプタが取れるように思ったけど、Win32の場合はWaitForMultipleObjects APIに与えた順となるので、完了順が 5 4 3 2 1 に配列に並んでいると最後に完了した5のハンドルが取れてとても困ることになったりする。僕はFreePeekを実装するとき、しょうがないので完了待ちに利用するハンドルを最古にIOを発行した1つに絞ってWaitForSingleObject APIを利用したなぁとか思い出した。
#世の中POSIXだけじゃなくてWin32も大きな勢力だから、まあ、どっちでもいいや。readやwriteの操作が同期されないIOをWin32では非同期(オーバーラップト、でも英語版でもasynchronousと書いてたりするわけだし)と呼び、POSIXではnon blockingと呼ぶ。重要な点は、マルチプレクサを利用するか、コールバックを利用するか、シグナルを利用するかといったプログラミングモデルのどれをどう使うとプログラミングが楽か、あるいは高速化できるか、という点だ。で、そのへんのバランスによって実装方法を決定し実装する。
あと、Win32でプログラミングをする場合には、Webサーバー限定で利用するのでも無い限りパフォーマンスを理由としてスケールさせることはそれほど無いから(例:ATM。それほどどころかスケールさせられるわけもなく)、同期を使うか非同期を使うかコールバックさせるかイベントを使うかとかは、すごく重要な問題で、どれを使ってもプログラミングできたほうが良かったりするので、当然、すごく気にする。
#そう言えばIO complation portを利用するっていう方法もあった(かつ一番効率が良いらしい(追記:ワーカスレッドのプールを使いやすいというのが理由なんじゃないかな? ReadFileExとかだと呼び出したスレッドが待ち状態にならないと呼ばれないから))。
Writing Windows NT Server Applications in MFC Using I/O Completion Ports。古い資料だが良い資料だ(9xのサポートがあるので利用できなかったような記憶が)。
ジェズイットを見習え |
こんにちは.Windows文化はさっぱりなので,非常に興味深いです.<br><br>> #世の中POSIXだけじゃなくてWin32も大きな勢力だから、まあ、どっちでもいいや。<br><br>成り行き上FreeBSDな私なのですが,正直なところドキュメントもそろってるし,サーバ用途ならばWindowsが最強なのでは?と思うことしばしです.
あとNonBlock+多重化とNonBlock+シグナルを区別しようってのはそうなんですが,今回のAsyncIOってそもそもあとから作られたモノなので,今までの「非同期IO」という文脈に割り込んで「オラオラ俺様が真の非同期IOだぜ」と主張するかのように「AsyncronousIO」という名前を使ってるのはどうかなぁとは思います.
まあ、そういう感じを受けないでもないですが、MS自身も.NET Framework上では非同期(asynchronous)という言葉をイベント(コールバック)ベースとIO Completion Portのようなものベースに限定しているので、むしろノンブロッキングなものを非同期と呼んでしまうWin32ネイティブな言い回しのほうが過去のものかなとも思います。Javaも(Unixベンダが多いから当然とは思うけど)マルチプレクサ方式とAsyncIO方式(http://jcp.org/en/jsr/detail?id=203)で呼び方を統一してますし、非同期=AsyncIOとしたほうが誤解は少ないですね。