使用overlapped I/O並搭配event對象-----win32多線程-非同步(asynchronous) I/O案例,會產生兩個基礎性問題。
第一個問題是,使用WaitForMultipleObjects(),你只能等待最多達MAXIMUM_WAIT_OBJECTS個對象,在Windows NT中此值最大為64。
第二個問題是,你必須不斷根據“那一個handle被激發”而計算如何反應。你必須有一個指派表格(dispatch table)和WaitForMultipleObjects()的handles數組結合起來。
這兩個問題可以靠一個所謂的非同步程序呼叫(asynchronous Procedure Calls, APCs)解決,只要使用“Ex”版的ReadFile()和WriteFile(),你就可以調用這個機制。這兩個函數允許你指定一個額外的參數,是一個callback函數,這個callback函數被稱為I/O completion routine,因為系統是在某一個特別的overlapped I/O操作完成之後調用它。
非同步程序呼叫案例:
/* * IoByAPC.c * * Sample code for Multithreading Applications in Win32 * This is from Chapter 6, Listing 6-3 * * Demonstrates how to use APC's (asynchronous * procedure calls) instead of signaled objects * to service multiple outstanding overlapped * operations on a file. */#define WIN32_LEAN_AND_MEAN#include <stdio.h>#include <stdlib.h>#include <windows.h>#include "MtVerify.h"//// Constants//#define MAX_REQUESTS 5#define READ_SIZE 512#define MAX_TRY_COUNT 5//// Function prototypes//void CheckOsVersion();int QueueRequest(int nIndex, DWORD dwLocation, DWORD dwAmount);//// Global variables//// Need a single event object so we know when all I/O is finishedHANDLE ghEvent;// Keep track of each individual I/O operationOVERLAPPED gOverlapped[MAX_REQUESTS];// Handle to the file of interest.HANDLE ghFile;// Need a place to put all this datachar gBuffers[MAX_REQUESTS][READ_SIZE];int nCompletionCount;/* * I/O Completion routine gets called * when app is alertable (in WaitForSingleObjectEx) * and an overlapped I/O operation has completed. */VOID WINAPI FileIOCompletionRoutine( DWORD dwErrorCode, // completion code DWORD dwNumberOfBytesTransfered, // number of bytes transferred LPOVERLAPPED lpOverlapped // pointer to structure with I/O information ){ // The event handle is really the user defined data int nIndex = (int)(lpOverlapped->hEvent); printf("Read #%d returned %d. %d bytes were read.\n", nIndex, dwErrorCode, dwNumberOfBytesTransfered); if (++nCompletionCount == MAX_REQUESTS) SetEvent(ghEvent); // Cause the wait to terminate}int main(){ int i; char szPath[MAX_PATH]; CheckOsVersion(); // Need to know when to stop MTVERIFY( ghEvent = CreateEvent( NULL, // No security TRUE, // Manual reset - extremely important! FALSE, // Initially set Event to non-signaled state NULL // No name ) ); GetWindowsDirectory(szPath, sizeof(szPath)); strcat(szPath, "\\WINHLP32.EXE"); // Open the file for overlapped reads ghFile = CreateFile( szPath, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL ); if (ghFile == INVALID_HANDLE_VALUE) { printf("Could not open %s\n", szPath); return -1; } // Queue up a few requests for (i=0; i<MAX_REQUESTS; i++) { // Read some bytes every few K QueueRequest(i, i*16384, READ_SIZE); } printf("QUEUED!!\n"); // Wait for all the operations to complete. for (;;) { DWORD rc; rc = WaitForSingleObjectEx(ghEvent, INFINITE, TRUE ); if (rc == WAIT_OBJECT_0) break; MTVERIFY(rc == WAIT_IO_COMPLETION); } CloseHandle(ghFile);getchar(); return EXIT_SUCCESS;}/* * Call ReadFileEx to start an overlapped request. * Make sure we handle errors that are recoverable. */int QueueRequest(int nIndex, DWORD dwLocation, DWORD dwAmount){ int i; BOOL rc; DWORD err; gOverlapped[nIndex].hEvent = (HANDLE)nIndex; gOverlapped[nIndex].Offset = dwLocation; for (i=0; i<MAX_TRY_COUNT; i++) { rc = ReadFileEx( ghFile, gBuffers[nIndex], dwAmount, &gOverlapped[nIndex], FileIOCompletionRoutine ); // Handle success if (rc) { // asynchronous i/o is still in progress printf("Read #%d queued for overlapped I/O.\n", nIndex); return TRUE; } err = GetLastError(); // Handle recoverable error if ( err == ERROR_INVALID_USER_BUFFER || err == ERROR_NOT_ENOUGH_QUOTA || err == ERROR_NOT_ENOUGH_MEMORY ) { Sleep(50); // Wait around and try later continue; } // Give up on fatal error. break; } printf("ReadFileEx failed.\n"); return -1;}//// Make sure we are running under an operating// system that supports overlapped I/O to files.//void CheckOsVersion(){ OSVERSIONINFO ver; BOOL bResult; ver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); bResult = GetVersionEx((LPOSVERSIONINFO) &ver); if ( (!bResult) || (ver.dwPlatformId != VER_PLATFORM_WIN32_NT) ) { fprintf(stderr, "IoByAPC must be run under Windows NT.\n");exit(EXIT_FAILURE); }}