非同步I/O是電腦作業系統對輸入輸出的一種處理方式:發起I/O請求的執行緒不等I/O操作完成,就繼續執行隨後的代碼,I/O結果用其他方式通知發起I/O請求的程式。與非同步I/O相對的是更為常見的「同步(阻塞)I/O」:發起I/O請求的執行緒不從正在呼叫的I/O操作函式返回(即被阻塞),直至I/O操作完成。

類Unix作業系統與POSIX

POSIX提供下述API函式:

More information 阻塞, 非阻塞 ...
阻塞 非阻塞
同步 write, read write, read + poll / select
非同步 - aio_write, aio_read
Close
  • aio[1]
  • io_uring (Linux 5.1以後支援)[2]

Windows作業系統的非同步I/O

Windows提供多種非同步I/O(也稱重疊I/O)方式:[3]

裝置核心對象

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 buffer[10] = {"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操作是否完成。[4]該函式檢查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 buffer[10] = {"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 buffer[10] = {"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完成对象

參見

參考文獻

外部連結

Wikiwand in your browser!

Seamless Wikipedia browsing. On steroids.

Every time you click a link to Wikipedia, Wiktionary or Wikiquote in your browser's search results, it will show the modern Wikiwand interface.

Wikiwand extension is a five stars, simple, with minimum permission required to keep your browsing private, safe and transparent.