異步I/O

異步I/O是計算機操作系統對輸入輸出的一種處理方式：發起I/O請求的線程不等I/O操作完成，就繼續執行隨後的代碼，I/O結果用其他方式通知發起I/O請求的程序。與異步I/O相對的是更為常見的“同步（阻塞）I/O”：發起I/O請求的線程不從正在調用的I/O操作函數返回（即被阻塞），直至I/O操作完成。


**類Unix操作系統與POSIX**
POSIX提供下述API函數：
*aio
*io_uring (Linux 5.1以後支援)

**Windows操作系統的異步I/O**
Windows提供多種異步I/O（也稱重疊I/O）方式：

**設備內核對象**
I/O設備在操作系統內核中表示為內核對象，因此具有可等待（waitable）內核對象狀態。例如：文件句柄，線程句柄等等。對於文件內核對象，當一個異步I/O完成後，該文件句柄被置為觸發態。使用這種方式獲取異步I/O完成的通知，缺點是如果在一個文件內核對象上同時有多個異步I/O操作，只通過文件句柄的觸發無法辨識哪個異步I/O操作完成了。
例子：
HANDLE hFile = CreateFileW(L"d:\\a.txt", GENERIC_WRITE, 0, 0, CREATE_ALWAYS, FILE_FLAG_OVERLAPPED, 0); //設置異步IO的標誌FILE_FLAG_OVERLAPPED
char buffer10 = {"abcd"};
OVERLAPPED ol = { 0 };//用0初始化OVERLAPPED的結構
ol.Offset = 2;/從文件的第三個字節開始IO
BOOL rt = WriteFile(hFile, buffer, 5, NULL, &ol);//發起一個異步寫操作
//SetFileCompletionNotificationModes(hFile, FILE_SKIP_SET_EVENT_ON_HANDLE);//如此設置則文件內核對象就不會被觸發
if (rt == FALSE && GetLastError() == ERROR_IO_PENDING)//檢查異步IO是否完成 
{
	WaitForSingleObject(hFile, INFINITE);//等待設備內核對象（文件）被觸發。
}
CloseHandle(hFile); 

**GetOverlappedResult函數**
也可以使用Windows API函數GetOverlappedResult直接阻塞/非阻塞等待指定的異步I/O操作是否完成。該函數檢查OVERLAPPED結構中的Internal成員的值是否為STATUS_PENDING來判斷異步I/O是否完成。 

**異步I/O操作的完成通知用事件內核對象**
在異步I/O操作的read/write函數調用中給出的OVERLAPPED類型的參數中，可以指定一個內核事件對象。這個異步I/O操作完成時，這個內核事件對象會被觸發。從而，等待在這個事件對象上的程序就會知道這個異步I/O操作完成。
例子：
HANDLE hFile = CreateFileW(L"d:\\a.txt", GENERIC_WRITE, 0, 0, CREATE_ALWAYS, FILE_FLAG_OVERLAPPED, 0); //設置異步IO的標誌FILE_FLAG_OVERLAPPED
char buffer10 = {"abcd"};
OVERLAPPED ol = { 0 };//用0初始化OVERLAPPED的結構
ol.Offset = 2;/從文件的第三個字節開始IO
HANDLE hEvent = CreateEvent(0, FALSE, FALSE, NULL);  
ol.hEvent = hEvent;//傳遞一個事件對象。  
BOOL rt = WriteFile(hFile, buffer, 5, NULL, &ol);//發起一個異步寫操作 
if (rt == FALSE && GetLastError() == ERROR_IO_PENDING)//檢查異步IO是否完成 
{
	WaitForSingleObject(ol.hEvent, INFINITE);//等待設備內核對象（文件）被觸發。
}
CloseHandle(hEvent); 
CloseHandle(hFile); 

**可喚醒I/O**
異步可喚醒I/O操作通過ReadFileEx/WriteFileEx函數指出完成過程回調函數。回調函數在該線程的可喚醒等待（alertable wait）中被執行。

**完成端口**
使用CreateIoCompletionPort函數創建一個完成端口。然後把文件句柄綁定到這個完成端口（通過CreateIoCompletionPort函數)。這個文件句柄上的異步I/O操作完成時，會自動向這個完成完成端口發通知。線程通過GetQueuedCompletionStatus函數等待這個完成端口上的完成通知，然後從GetQueuedCompletionStatus的調用返回處恢復線程執行。

**線程池I/O完成對象**
使用CreateThreadpoolIo函數創建一個I/O完成對象，綁定了要執行異步I/O操作的文件句柄與待執行的回調函數。通過StartThreadpoolIo函數開始I/O完成對象的工作。每當綁定的文件句柄上的異步I/O操作完成，自動調用線程池上的線程執行指定的回調函數。
例子：
VOID CALLBACK OverlappedCompletionRoutine(PTP_CALLBACK_INSTANCE pInstance,  
PVOID pvContext,  
PVOID pOverlapped,  
ULONG IoResult,  
ULONG_PTR NumberOfBytesTransferred,  
PTP_IO pIo)  
{  
printf("OverlappedCompletionRoutine, transferred: %d bytes\n", NumberOfBytesTransferred);  
}  
HANDLE hFile = CreateFileW(L"d:\\a.txt", GENERIC_WRITE, 0, 0, CREATE_ALWAYS, FILE_FLAG_OVERLAPPED, 0); //設置異步IO的標誌FILE_FLAG_OVERLAPPED
PTP_IO pio = CreateThreadpoolIo(hFile, OverlappedCompletionRoutine, NULL, NULL);//將設備對象和線程池的IO完成端口關聯起來。  
StartThreadpoolIo(pio);
char buffer10 = {"abcd"};
OVERLAPPED ol = { 0 };//用0初始化OVERLAPPED的結構
ol.Offset = 2;/從文件的第三個字節開始IO   
BOOL rt = WriteFile(hFile, buffer, 5, NULL, &ol);//發起一個異步寫操作 
if(rt==FALSE && GetLastError()==ERROR_IO_PENDING))
{  
::Sleep(4000); 
//do somethings... 
}
else
{
CancelThreadpoolIo(pio); 
}
WaitForThreadpoolIoCallbacks(pio,false);
CloseHandle(hFile); 
CloseThreadpoolIo(pio);//關閉線程池io完成對象

**參見**
IOCP 

**參考文獻**

**外部鏈接**
 The C10K Problem; a survey of asynchronous I/O methods with emphasis on scaling – by Dan Kegel
*Article " Boost application performance using asynchronous I/O date=20160422074109 " by M. Tim Jones
*Article " Lazy Asynchronous I/O For Event-Driven Servers date=20120110022450 " by Willy Zwaenepoel, Khaled Elmeleegy, Anupam Chanda and Alan L. Cox- Perform I/O Operations in Parallel date=20171225180852 - Description from POSIX standard date=20101205142322 - Inside I/O Completion Ports by Mark Russinovich- Asynchronous I/O and The Asynchronous Disk I/O Explorer date=20171222112320 - I/O::AI/O is a Perl module offering an asynchronous interface for most I/O operations date=20171012094128 - ACE Proactor