E/S síncrona e assíncrona
Consulte também Aplicativos de exemplo relacionados a E/S.
Existem dois tipos de sincronização de entrada/saída (E/S): E/S síncrona e E/S assíncrona. A E/S assíncrona também é chamada de E/S sobreposta.
Na E/S de arquivo síncrona, um thread inicia uma operação de E/S e imediatamente entra em um estado de espera até que a solicitação de E/S seja concluída. Um thread que executa a E/S de arquivo assíncrona envia uma solicitação de E/S para o kernel chamando uma função apropriada. Se a solicitação for aceita pelo kernel, o thread de chamada continuará processando outro trabalho até que o kernel sinalize ao thread de que a operação de E/S foi concluída. Em seguida, ele interrompe seu trabalho atual e processa os dados da operação de E/S conforme necessário.
Os dois tipos de sincronização são ilustrados na figura a seguir.
Quando é esperado que uma solicitação de E/S leve muito tempo, como uma atualização ou backup de um banco de dados grande ou um link de comunicação lento, a E/S assíncrona costuma ser uma boa maneira de otimizar a eficiência do processamento. No entanto, para operações de E/S relativamente rápidas, a sobrecarga de processamento de solicitações de E/S do kernel e de sinais de kernel pode tornar a E/S assíncrona menos interessante, especialmente se for necessário fazer várias operações de E/S rápidas. Nesse caso, a E/S síncrona seria mais indicada. Os mecanismos e os detalhes de implementação de como realizar essas tarefas variam dependendo do tipo de identificador de dispositivo usado e das necessidades específicas do aplicativo. Em outras palavras, geralmente existem várias maneiras de resolver o problema.
Considerações de E/S síncrona e assíncrona
Se um arquivo ou dispositivo for aberto para E/S síncrona (ou seja, FILE_FLAG_OVERLAPPED não for especificado), as chamadas subsequentes para funções como WriteFile poderão bloquear a execução do thread de chamada até ocorra um dos seguintes eventos:
- A operação de E/S é concluída (uma gravação de dados, neste exemplo).
- Ocorre um erro de E/S. (Por exemplo, o pipe é fechado na outra extremidade.)
- Ocorreu um erro na própria chamada (por exemplo, um ou mais parâmetros não são válidos).
- Outro thread no processo chama a função CancelSynchronousIo usando o identificador de thread do thread bloqueado, que encerra a E/S para esse thread, causando a falha na operação de E/S.
- O thread bloqueado é encerrado pelo sistema; por exemplo, o processo em si é encerrado ou outro thread chama a função TerminateThread usando o identificador do thread bloqueado. (Isso geralmente é considerado um último recurso e não um bom design de aplicativo.)
Em alguns casos, esse atraso pode ser inaceitável para o design e a finalidade do aplicativo, e os designers de aplicativos devem considerar o uso de E/S assíncrona com objetos de sincronização de thread apropriados, tais como portas de conclusão de E/S. Para obter mais informações sobre sincronização de threads, consulte Sobre sincronização.
Um processo abre um arquivo para E/S assíncrona em sua chamada para CreateFile especificando o sinalizador FILE_FLAG_OVERLAPPED no parâmetro dwFlagsAndAttributes. Se FILE_FLAG_OVERLAPPED não for especificado, o arquivo será aberto para a E/S síncrona. Quando o arquivo é aberto para E/S assíncrona, um ponteiro para uma estrutura OVERLAPPED é passada na chamada para ReadFile e WriteFile. Ao executar a E/S síncrona, essa estrutura não é necessária em chamadas para ReadFile e WriteFile.
Observação
Se um arquivo ou dispositivo for aberto para a E/S assíncrona, as chamadas subsequentes para funções como WriteFile usando esse identificador geralmente retornarão imediatamente, mas também poderão se comportar de forma síncrona em relação à execução bloqueada. Para mais informações, consulte E/S assíncrona de disco aparece como síncrona no Windows.
Embora CreateFile seja a função mais comum a ser usada para abrir arquivos, volumes de disco, pipes anônimos e outros dispositivos semelhantes, as operações de E/S também podem ser executadas usando um typecast typecast de outros objetos do sistema, como um soquete criado pela função inserir ou aceitar.
Os identificadores para objetos do directory são obtidos ao chamar a função CreateFile com o atributo FILE_FLAG_BACKUP_SEMANTICS. Os identificadores de diretório quase nunca são usados; aplicativos de backup são um dos poucos que normalmente os usam.
Depois de abrir o objeto de arquivo para E/S assíncrona, é necessário criar uma estrutura OVERLAPPED, inicializado e passado corretamente em cada chamada para funções como ReadFile e WriteFile. Lembre-se do seguinte ao usar a estrutura OVERLAPPED em operações assíncronas de leitura e gravação:
- Não desaloque ou modifique a estrutura OVERLAPPED ou o buffer de dados até que todas as operações de E/S assíncronas para o objeto de arquivo tenham sido concluídas.
- Se você declarar o ponteiro para a estrutura OVERLAPPED como uma variável local, não saia da função local até que todas as operações de E/S assíncronas para o objeto de arquivo tenham sido concluídas. Se a função local for encerrada prematuramente, a estrutura OVERLAPPED sairá do escopo e ficará inacessível a qualquer função ReadFile ou WriteFile que ela encontrar fora dessa função.
Você também pode criar um evento e colocar o identificador na estrutura estrutura OVERLAPPED; as funções de espera de espera podem ser usadas para aguardar a conclusão da operação de E/S aguardando o identificador de evento.
Conforme já mencionado, ao trabalhar com um identificador assíncrono, os aplicativos devem ter cuidado ao fazer determinações sobre quando liberar recursos associados a uma operação de E/S especificada nesse identificador. Se o identificador for desalocado prematuramente, ReadFile ou WriteFile poderá relatar incorretamente que a operação de E/S foi concluída. Além disso, às vezes a função WriteFile retornará TRUE com um valor GetLastError de ERROR_SUCCESS, mesmo que esteja usando um identificador assíncrono (que também pode retornar FALSE com ERROR_IO_PENDING). Os programadores que estão acostumados ao design de E/S síncrona geralmente liberam recursos de buffer de dados neste ponto porque TRUE e ERROR_SUCCESS significam que a operação foi concluída. No entanto, se as portas de conclusão de E/S estiverem sendo usadas com esse identificador assíncrono, um pacote de conclusão também será enviado, mesmo que a operação de E/S tenha sido concluída imediatamente. Em outras palavras, se o aplicativo liberar recursos depois que WriteFile retornar TRUE com ERROR_SUCCESS além da rotina de porta de conclusão de E/S, ele terá uma condição de erro double-free. Neste exemplo, a recomendação seria permitir que a rotina de porta de conclusão seja a única responsável por todas as operações de liberação desses recursos.
O sistema não mantém o ponteiro de arquivo em identificadores assíncronos para arquivos e dispositivos que dão suporte a ponteiros de arquivo (ou seja, dispositivos de busca), portanto, a posição do arquivo deve ser passada para as funções de leitura e gravação nos membros de dados de deslocamento relacionados da estrutura OVERLAPPED. Para obter mais informações, consulte WriteFile e ReadFile.
A posição do ponteiro de arquivo para um identificador síncrono é mantida pelo sistema à medida que os dados são lidos ou gravados e também pode ser atualizada usando a função SetFilePointer ou SetFilePointerEx.
Um aplicativo também pode aguardar o identificador de arquivo para sincronizar a conclusão de uma operação de E/S, mas isso requer muito cuidado. Cada vez que uma operação de E/S é iniciada, o sistema operacional define o identificador de arquivo para o estado não sinalizado. Cada vez que uma operação de E/S é concluída, o sistema operacional define o identificador de arquivo para o estado não sinalizado. Portanto, se um aplicativo iniciar duas operações de E/S e aguardar o identificador do arquivo, não haverá como determinar qual operação será concluída quando o identificador for definido para o estado sinalizado. Se um aplicativo precisar executar várias operações de E/S assíncronas em um único arquivo, deverá aguardar o identificador de evento na estrutura OVERLAPPED para cada operação de E/S em vez de no identificador de arquivo.
Para cancelar todas as operações de E/S assíncronas pendentes, use:
- CancelIo — essa função cancela apenas as operações emitidas pelo thread de chamada para o identificador de arquivo especificado.
- CancelIoEx — essa função cancela todas as operações emitidas pelos threads para o identificador de arquivo especificado.
Use CancelSynchronousIo para cancelar operações de E/S síncrona pendentes.
As funções ReadFileEx and WriteFileEx permitem que um aplicativo especifique a rotina que será executada (consulte FileIOCompletionRoutine) quando a solicitação de E/S assíncrona for concluída.