I/O sincrono e asincrono

Vedere anche applicazioni di esempio correlate all'I/O.

Esistono due tipi di sincronizzazione di input/output (I/O): I/O sincrono e I/O asincrono. L'I/O asincrona viene anche definita I/O sovrapposta.

Nel file sincrono I/O, un thread avvia un'operazione di I/O e passa immediatamente a uno stato di attesa fino al completamento della richiesta di I/O. Un thread che esegue l'I/O di un file asincrono invia una richiesta di I/O al kernel chiamando una funzione appropriata. Se la richiesta viene accettata dal kernel, il thread chiamante continua l'elaborazione di un altro processo finché il kernel non segnala al thread che l'operazione di I/O è stata completata. Interrompe quindi il processo corrente ed elabora i dati dall'operazione di I/O in base alle esigenze.

I due tipi di sincronizzazione sono illustrati nella figura seguente.

i/o sincrono e asincrono

Nelle situazioni in cui si prevede che una richiesta di I/O richieda molto tempo, ad esempio un aggiornamento o un backup di un database di grandi dimensioni o un collegamento di comunicazione lenta, l'I/O asincrona è in genere un buon modo per ottimizzare l'efficienza di elaborazione. Tuttavia, per le operazioni di I/O relativamente veloci, il sovraccarico dell'elaborazione delle richieste di I/O del kernel e dei segnali del kernel può rendere l'I/O asincrona meno vantaggiosa, in particolare se è necessario eseguire molte operazioni di I/O veloci. In questo caso, l'I/O sincrono sarebbe migliore. I meccanismi e i dettagli di implementazione su come eseguire queste attività variano a seconda del tipo di handle di dispositivo usato e delle specifiche esigenze dell'applicazione. In altre parole, esistono in genere più modi per risolvere il problema.

Considerazioni sulle operazioni di I/O sincrone e asincrone

Se un file o un dispositivo viene aperto per operazioni di I/O sincrone (ovvero , FILE_FLAG_OVERLAPPED non viene specificato), le chiamate successive a funzioni come WriteFile possono bloccare l'esecuzione del thread chiamante fino a quando non si verifica uno degli eventi seguenti:

  • L'operazione di I/O viene completata (in questo esempio, una scrittura di dati).
  • Si è verificato un errore di I/O. Ad esempio, la pipe viene chiusa dall'altra estremità.
  • Si è verificato un errore nella chiamata stessa (ad esempio, uno o più parametri non sono validi).
  • Un altro thread nel processo chiama la funzione CancelSynchronousIo usando l'handle di thread bloccato, che termina l'I/O per tale thread, con esito negativo dell'operazione di I/O.
  • Il thread bloccato viene terminato dal sistema; Ad esempio, il processo stesso viene terminato o un altro thread chiama la funzione TerminateThread usando l'handle del thread bloccato. In genere si tratta di un'ultima risorsa e non di una buona progettazione di applicazioni.

In alcuni casi, questo ritardo può essere inaccettabile per la progettazione e lo scopo dell'applicazione, pertanto i progettisti di applicazioni devono prendere in considerazione l'uso di operazioni di I/O asincrone con oggetti di sincronizzazione thread appropriati, ad esempio le porte di completamento di I/O. Per altre informazioni sulla sincronizzazione dei thread, vedere Informazioni sulla sincronizzazione.

Un processo apre un file per le operazioni di I/O asincrone nella chiamata a CreateFile specificando il flag FILE_FLAG_OVERLAPPED nel parametro dwFlagsAndAttributes . Se non viene specificato FILE_FLAG_OVERLAPPED, il file viene aperto per le operazioni di I/O sincrone. Quando il file è stato aperto per l'I/O asincrono, un puntatore a una struttura OVERLAPPED viene passato alla chiamata a ReadFile e WriteFile. Quando si eseguono operazioni di I/O sincrone, questa struttura non è necessaria nelle chiamate a ReadFile e WriteFile.

Nota

Se un file o un dispositivo viene aperto per operazioni di I/O asincrone, le chiamate successive a funzioni come WriteFile che usano tale handle restituiscono in genere immediatamente, ma possono comportarsi in modo sincrono rispetto all'esecuzione bloccata. Per altre informazioni, vedere https://support.microsoft.com/kb/156932.

 

Sebbene CreateFile sia la funzione più comune da usare per l'apertura di file, volumi di dischi, pipe anonime e altri dispositivi simili, le operazioni di I/O possono essere eseguite anche usando un typecast di handle da altri oggetti di sistema, ad esempio un socket creato dal socket o funzioni accept .

Gli handle per gli oggetti directory vengono ottenuti chiamando la funzione CreateFile con l'attributo FILE_FLAG_BACKUP_SEMANTICS . Gli handle di directory non vengono quasi mai usati. Le applicazioni di backup sono una delle poche applicazioni che in genere li useranno.

Dopo aver aperto l'oggetto file per le operazioni di I/O asincrone, è necessario creare, inizializzare e passare correttamente una struttura OVERLAPPED a funzioni quali ReadFile e WriteFile. Quando si usa la struttura OVERLAPPED nelle operazioni di lettura e scrittura asincrone, tenere presente quanto segue:

  • Non deallocare o modificare la struttura OVERLAPPED o il buffer di dati fino al completamento di tutte le operazioni di I/O asincrone nell'oggetto file.
  • Se si dichiara il puntatore alla struttura OVERLAPPED come variabile locale, non uscire dalla funzione locale fino al completamento di tutte le operazioni di I/O asincrone all'oggetto file. Se la funzione locale viene chiusa in modo anomalo, la struttura OVERLAPPED esce dall'ambito e sarà inaccessibile a qualsiasi funzione ReadFile o WriteFile rilevata all'esterno di tale funzione.

È anche possibile creare un evento e inserire l'handle nella struttura OVERLAPPED ; Le funzioni di attesa possono quindi essere usate per attendere il completamento dell'operazione di I/O attendendo l'handle dell'evento.

Come indicato in precedenza, quando si usa un handle asincrono, le applicazioni devono prestare attenzione quando liberare le risorse associate a un'operazione di I/O specificata su tale handle. Se l'handle viene deallocato in modo anomalo, ReadFile o WriteFile potrebbe segnalare erroneamente che l'operazione di I/O è stata completata. Inoltre, la funzione WriteFile restituisce talvolta TRUE con un valore GetLastError di ERROR_SUCCESS, anche se usa un handle asincrono (che può anche restituire FALSE con ERROR_IO_PENDING). I programmatori abituati alla progettazione sincrona di I/O in genere rilasciano le risorse del buffer dei dati a questo punto perché TRUE e ERROR_SUCCESS indicano che l'operazione è stata completata. Tuttavia, se le porte di completamento di I/O vengono usate con questo handle asincrono, verrà inviato anche un pacchetto di completamento anche se l'operazione di I/O è stata completata immediatamente. In altre parole, se l'applicazione libera le risorse dopo writeFile restituisce TRUE con ERROR_SUCCESS oltre alla routine della porta di completamento I/O, avrà una condizione di errore doppio. In questo esempio, è consigliabile consentire alla routine della porta di completamento di essere responsabile esclusivamente di tutte le operazioni di liberamento per tali risorse.

Il sistema non gestisce il puntatore di file su handle asincroni a file e dispositivi che supportano puntatori ai file (ovvero alla ricerca di dispositivi), pertanto la posizione del file deve essere passata alle funzioni di lettura e scrittura nei membri dati di offset correlati della struttura OVERLAPPED . Per altre informazioni, vedere WriteFile e ReadFile.

La posizione del puntatore file per un handle sincrono viene gestita dal sistema quando i dati vengono letti o scritti e possono essere aggiornati anche tramite la funzione SetFilePointer o SetFilePointerEx .

Un'applicazione può anche attendere l'handle di file per sincronizzare il completamento di un'operazione di I/O, ma questa operazione richiede estrema cautela. Ogni volta che viene avviata un'operazione di I/O, il sistema operativo imposta l'handle di file sullo stato non firmato. Ogni volta che viene completata un'operazione di I/O, il sistema operativo imposta l'handle di file sullo stato segnalato. Pertanto, se un'applicazione avvia due operazioni di I/O e attende l'handle di file, non è possibile determinare quale operazione viene completata quando l'handle è impostato sullo stato segnalato. Se un'applicazione deve eseguire più operazioni di I/O asincrone su un singolo file, deve attendere l'handle dell'evento nella struttura OVERLAPPED specifica per ogni operazione di I/O anziché sull'handle di file comune.

Per annullare tutte le operazioni di I/O asincrone in sospeso, usare:

  • CancelIo: questa funzione annulla solo le operazioni eseguite dal thread chiamante per l'handle di file specificato.
  • CancelIoEx: questa funzione annulla tutte le operazioni eseguite dai thread per l'handle di file specificato.

Usare CancelSynchronousIo per annullare le operazioni di I/O sincrone in sospeso.

Le funzioni ReadFileEx e WriteFileEx consentono a un'applicazione di specificare una routine da eseguire (vedere FileIOCompletionRoutine) al termine della richiesta di I/O asincrona.