Synkron och asynkron I/O
Se även I/O-relaterade exempelprogram.
Det finns två typer av indata-/utdatasynkronisering (I/O): synkron I/O och asynkron I/O. Asynkron I/O kallas även för överlappande I/O.
I synkrona fil-I/O-startar en tråd en I/O-åtgärd och anger omedelbart ett väntetillstånd tills I/O-begäran har slutförts. En tråd som utför asynkrona fil-I/O- skickar en I/O-begäran till kerneln genom att anropa en lämplig funktion. Om begäran godkänns av kerneln fortsätter den anropande tråden att bearbeta ett annat jobb tills kerneln signalerar till tråden att I/O-åtgärden är klar. Den avbryter sedan sitt aktuella jobb och bearbetar data från I/O-åtgärden efter behov.
De två synkroniseringstyperna visas i följande bild.
I situationer där en I/O-begäran förväntas ta lång tid, till exempel en uppdatering eller säkerhetskopiering av en stor databas eller en långsam kommunikationslänk, är asynkron I/O i allmänhet ett bra sätt att optimera bearbetningseffektiviteten. För relativt snabba I/O-åtgärder kan dock arbetet med att bearbeta kernel-I/O-begäranden och kernelsignaler göra asynkrona I/O mindre fördelaktigt, särskilt om många snabba I/O-åtgärder behöver utföras. I det här fallet skulle synkron I/O vara bättre. Mekanismerna och implementeringsinformationen för hur du utför dessa uppgifter varierar beroende på vilken typ av enhetshandtag som används och programmets specifika behov. Med andra ord finns det vanligtvis flera sätt att lösa problemet.
Synkrona och asynkrona I/O-överväganden
Om en fil eller enhet öppnas för synkron I/O (d.v.s. FILE_FLAG_OVERLAPPED inte har angetts) kan efterföljande anrop till funktioner som WriteFile blockera körning av den anropande tråden tills någon av följande händelser inträffar:
- I/O-åtgärden slutförs (i det här exemplet en dataskrivning).
- Ett I/O-fel inträffar. (Till exempel stängs röret från den andra änden.)
- Ett fel uppstod i själva anropet (till exempel är en eller flera parametrar ogiltiga).
- En annan tråd i processen anropar funktionen CancelSynchronousIo med hjälp av den blockerade trådens trådhandtag, vilket avslutar I/O för den tråden och misslyckas med I/O-åtgärden.
- Den blockerade tråden avslutas av systemet. Till exempel avslutas själva processen, eller så anropar en annan tråd funktionen TerminateThread med hjälp av den blockerade trådens handtag. (Detta anses allmänt vara en sista utväg och inte bra programdesign.)
I vissa fall kan den här fördröjningen vara oacceptabel för programmets design och syfte, så programdesigners bör överväga att använda asynkron I/O med lämpliga trådsynkroniseringsobjekt som I/O-slutförandeportar. Mer information om trådsynkronisering finns i Om synkronisering.
En process öppnar en fil för asynkron I/O i anropet till CreateFile genom att ange flaggan FILE_FLAG_OVERLAPPED i parametern dwFlagsAndAttributes. Om FILE_FLAG_OVERLAPPED inte har angetts öppnas filen för synkron I/O. När filen har öppnats för asynkron I/O skickas en pekare till en OVERLAPPED- struktur i anropet till ReadFile och WriteFile. När du utför synkron I/O krävs inte den här strukturen i anrop till ReadFile och WriteFile.
Not
Om en fil eller enhet öppnas för asynkron I/O returnerar efterföljande anrop till funktioner som WriteFile som använder handtaget vanligtvis omedelbart men kan också bete sig synkront med avseende på blockerad körning. Mer information finns i Asynkron disk-I/O visas som synkron i Windows.
Även om CreateFile är den vanligaste funktionen att använda för att öppna filer, diskvolymer, anonyma rör och andra liknande enheter, kan I/O-åtgärder också utföras med hjälp av en referens typecast- från andra systemobjekt, till exempel en socket som skapats av socket eller acceptera funktioner.
Referenser till katalogobjekt hämtas genom att anropa funktionen CreateFile med attributet FILE_FLAG_BACKUP_SEMANTICS. Katalogreferenser används nästan aldrig – säkerhetskopieringsprogram är ett av de få program som vanligtvis använder dem.
När du har öppnat filobjektet för asynkront I/O måste en OVERLAPPED- struktur skapas korrekt, initieras och skickas till varje anrop till funktioner som ReadFile och WriteFile. Tänk på följande när du använder OVERLAPPED struktur i asynkrona läs- och skrivåtgärder:
- Frigör eller ändra inte OVERLAPPED- struktur eller databufferten förrän alla asynkrona I/O-åtgärder till filobjektet har slutförts.
- Om du deklarerar pekaren till OVERLAPPED struktur som en lokal variabel ska du inte avsluta den lokala funktionen förrän alla asynkrona I/O-åtgärder till filobjektet har slutförts. Om den lokala funktionen avslutas i förtid kommer OVERLAPPED--strukturen att gå utanför omfånget och den kommer inte att vara tillgänglig för alla ReadFile- eller WriteFile funktioner som den stöter på utanför funktionen.
Du kan också skapa en händelse och placera handtaget i OVERLAPPED- struktur. väntefunktionerna kan sedan användas för att vänta tills I/O-åtgärden har slutförts genom att vänta på händelsehandtaget.
Som tidigare nämnts bör program vara försiktiga när de arbetar med ett asynkront handtag när de bestämningar om när du ska frigöra resurser som är associerade med en angiven I/O-åtgärd på handtaget. Om handtaget frigörs i förtid kan ReadFile eller WriteFile felaktigt rapportera att I/O-åtgärden är klar. Funktionen WriteFile returnerar ibland TRUE- med ett GetLastError- värde för ERROR_SUCCESS, även om den använder ett asynkront handtag (som också kan returnera FALSE- med ERROR_IO_PENDING). Programmerare som är vana vid synkron I/O-design släpper vanligtvis databuffertresurser i det här läget eftersom TRUE- och ERROR_SUCCESS betyder att åtgärden är klar. Men om I/O-slutförandeportar används med det här asynkrona handtaget skickas även ett slutförandepaket trots att I/O-åtgärden slutfördes omedelbart. Med andra ord, om programmet frigör resurser efter WriteFile- returnerar TRUE- med ERROR_SUCCESS utöver I/O-slutförandeporten, kommer det att ha ett dubbelfritt feltillstånd. I det här exemplet skulle rekommendationen vara att tillåta att slutförandeportrutinen är ensam ansvarig för alla frigöringsåtgärder för sådana resurser.
Systemet underhåller inte filpekaren på asynkrona referenser till filer och enheter som stöder filpekare (dvs. söker enheter), därför måste filpositionen skickas till läs- och skrivfunktionerna i de relaterade förskjutningsdatamedlemmarna i OVERLAPPED- struktur. Mer information finns i WriteFile och ReadFile.
Filpekarpositionen för ett synkront handtag underhålls av systemet när data läse eller skrivs och kan också uppdateras med hjälp av funktionen SetFilePointer eller SetFilePointerEx.
Ett program kan också vänta på filhandtaget för att synkronisera slutförandet av en I/O-åtgärd, men det kräver extrem försiktighet. Varje gång en I/O-åtgärd startas anger operativsystemet filhandtaget till det icke-signalerade tillståndet. Varje gång en I/O-åtgärd slutförs anger operativsystemet filhandtaget till det signalerade tillståndet. Om ett program startar två I/O-åtgärder och väntar på filhandtaget finns det därför inget sätt att avgöra vilken åtgärd som är klar när handtaget är inställt på det signalerade tillståndet. Om ett program måste utföra flera asynkrona I/O-åtgärder på en enskild fil bör det vänta på händelsehandtaget i den specifika OVERLAPPED- struktur för varje I/O-åtgärd i stället för på det gemensamma filhandtaget.
Om du vill avbryta alla väntande asynkrona I/O-åtgärder använder du antingen:
- CancelIo– den här funktionen avbryter endast åtgärder som utfärdats av den anropande tråden för det angivna filhandtaget.
- CancelIoEx– den här funktionen avbryter alla åtgärder som utfärdas av trådarna för det angivna filhandtaget.
Använd CancelSynchronousIo för att avbryta väntande synkrona I/O-åtgärder.
Funktionerna ReadFileEx och WriteFileEx gör det möjligt för ett program att ange en rutin att köra (se FileIOCompletionRoutine) när den asynkrona I/O-begäran har slutförts.