非同期IO

非同期I/O () とは、入出力の処理を、その要請元のプロセス・スレッドとは独立に（非同期に）行う、入出力のAPIの類型である。
概要.
ブロッキング・非ブロッキングとの違い.
非同期I/Oはほぼ必ず非ブロッキングI/O (non-blocking I/O) であるため、非常にしばしば混同されるが、同期 or 非同期と、ブロッキング or 非ブロッキングという分類は、必ずしも一致しない。POSIX環境において、codice_1が設定されたファイル記述子に対して通常のreadやwriteを行うと非ブロッキングになるが、それは「ブロックされるようであればエラーにする」という動作になるのであって、非同期になるのではない（たいていのI/O操作はOS内のバッファなどによって、同期型のAPIでもブロックすることなく完了できることも多い）。ディスクに実際に書き込まれるまでを待つかどうか、という観点での同期・非同期もあるが、それはここで扱っているものとは別の話である（詳細は文献等の、フラグ codice_2, codice_3 についての記述や英語版記事 などを参照のこと）。
非同期I/Oとは、
といったようなスタイルの入出力APIによるI/Oである。よって非同期I/Oが利用されるのは、「時間制約の厳しいRTOSだから」といったような理由ではない。排他制御の都合などでブロックさせられないとか、あるいは、性能上の理由ではエンタープライズ用途で欲されることもあれば、イベントドリブン型のフレームワークであるために必要であるといった場合もある。別スレッドを使うことで、プロセス内で非同期I/Oのように見せかけるライブラリ（フレームワーク）といったものもあり得る。
実装.
Linuxでは、POSIX-XSI、POSIX 1003.1bあるいはio_uringの実装が行われている。
Windowsでは、Windows NT系列 (Windows NT 3.1以降) の全てのバージョンで実装が行われている。
使用方法.
基本的な呼び出し方法は、以下のステップである（前提とするAPIによって異なる。ここで示すのは一例）。
組込型RTOSやミニコンピュータでの実装は、非同期システムコール発行時に、システムサービスコールをOSの処理として行うが、UNIXやLinuxの処理では、ユーザースレッドとして処理を行っている実装が多い。
下記の例では、codice_4に続いて、codice_5を実行しているため、実質的にwrite()と同じになる。
Linuxでのサンプルプログラム.
/* 非同期I/Oによるファイル出力の例 */
int main(void)
 int fd;
 int n, status;
 unsigned char *Aio_buff[DATA_BUF_NUM];
 struct aiocb Aiocb[DATA_BUF_NUM];
 struct aiocb *List[DATA_BUF_NUM];
 if ((fd = open("datafile", (O_CREAT | O_WRONLY), 0666)) < 0)
 exit(1);
 /* リンクリストの作成 */
 for (n = 0; n < DATA_BUF_NUM; n++)
 Aio_buff[n] = (unsigned char*)memalign(sysconf(_SC_PAGESIZE), DATA_BUF_SIZE);
 memset((void *)(Aio_buff[n]), n, DATA_BUF_SIZE); /* データはページ単位にnで埋めている */
 Aiocb[n].aio_buf = Aio_buff[n];
 Aiocb[n].aio_offset = (long long)(DATA_BUF_SIZE * n);
 Aiocb[n].aio_nbytes = (long long)DATA_BUF_SIZE;
 Aiocb[n].aio_fildes = fd;
 Aiocb[n].aio_reqprio = 0;
 Aiocb[n].aio_lio_opcode = LIO_WRITE;
 Aiocb[n].aio_sigevent.sigev_notify = SIGEV_NONE;
 List[n] = &Aiocb[n];
 /* 非同期I/Oの発行 */
 lio_listio(LIO_WAIT, (struct aiocb **)&List[0], DATA_BUF_NUM, &sig);
 /* この間ファイル出力と並行して、別の処理を記述できる */
 /* 非同期I/Oの終了待ち */
 aio_suspend((const struct aiocb **)&List[0], DATA_BUF_NUM, &timeout);
 /* エラーステータスの確認 */
 for (n = 0; n < DATA_BUF_NUM; n++)
 status = aio_error(&(Aiocb[n]));
 if (status) printf("%d is error %d:%s\n", n, status, strerror(status));
 /* 終了ステータスの確認 */
 for (n = 0; n < DATA_BUF_NUM; n++)
 status = aio_return(&(Aiocb[n]));
 if (status != DATA_BUF_SIZE) printf("%d is write error\n", n);
 close(fd);
 return 0;
Windows.
Microsoft Windows環境では、Windows NT 3.1以降の全てのディスクI/O、Winsock (バージョン2.0以降) などのWindows APIに非同期バージョンの関数がいくつか用意されている。例えばReadFileやWriteFile APIは、OVERLAPPED構造体に非同期I/Oのための現在のコンテキストを保持し、結果を待機するためのカーネルシグナルオブジェクトを指定することができる。
より高度な実装を行う場合は、これらのシグナルオブジェクトとスレッドを動的に管理する「I/O完了ポート」を利用し、最適なワーカースレッド数の制御とI/OオフロードをAPIレベルで実現できる。
非同期プログラミング環境.
コールバックなどを利用した非同期I/Oの結果の取得は、設計やプログラミングが煩雑になる。特に出力よりも入力で問題が大きい。非同期I/Oに限った話ではないが、一般的な非同期処理を記述する「非同期プログラミング」を容易にするために、ライブラリや言語構文によるサポートが用意されているプログラミング環境もある。
ライブラリによるサポートは、例えばJava 1.5にて標準化されたや、C++11にて標準化されたcodice_6/codice_7などがあり、これらはスレッドベースのFuture パターン (Promise) を実現する。.NET Framework/.NET CoreではC#などの.NET言語から利用可能な (Task Parallel Library, TPL) が、またMicrosoft Visual C++の同時実行ランタイムではC++から利用可能な (Parallel Patterns Library, PPL) が用意されている。さらにこれらを発展させたものとして、C# 5.0/VB.NET 11以降や、Python 3.5以降にはasync/await構文が用意されている。なお、F#には非同期ワークフロー (asynchronous workflow) と呼ばれる、TPLとは異なる独自のインフラを利用した非同期プログラミングのための機能が備わっている。C++ではC++20にてcodice_8構文が標準化された。
JavaScriptはシングルスレッドで動作するため、非同期プログラミングはイベント駆動ベースの疑似的な手法に頼らざるを得なかったが、Web Workerによってマルチスレッドプログラミングがサポートされるようになった（Web Workerのスレッドではメモリ空間を共有せず、実際にはメッセージベースのマルチプロセスプログラミングとなる）。そのほか、ECMAScript 2015 (ES2015) ではPromiseが、ES2017ではasync/await構文が標準化された。