E/S síncrona e assíncrona

Consulte também aplicativos de exemplo relacionados a E/S.

Há dois tipos de sincronização de E/S (entrada/saída): E/S síncrona e E/S assíncrona. E/S assíncrona também é conhecida como E/S sobreposta.

Na E/S de arquivo síncrona, um thread inicia uma operação de E/S e entra imediatamente em um estado de espera até que a solicitação de E/S seja concluída. Um thread executando E/S de arquivo assíncrono 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 para o thread que a operação de E/S está 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.

E/S síncrona e assíncrona

Em situações em que uma solicitação de E/S deve levar muito tempo, como uma atualização ou backup de um banco de dados grande ou um link de comunicações lentas, a E/S assíncrona geralmente é uma boa maneira de otimizar a eficiência de processamento. No entanto, para operações de E/S relativamente rápidas, a sobrecarga de processamento de solicitações de E/S de kernel e sinais de kernel pode tornar a E/S assíncrona menos benéfica, especialmente se muitas operações de E/S rápidas precisarem ser feitas. Nesse caso, e/S síncrona seria melhor. Os mecanismos e 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 há várias maneiras de resolver o problema.

Considerações de E/S síncronas e assíncronas

Se um arquivo ou dispositivo for aberto para E/S síncrona (ou seja, FILE_FLAG_OVERLAPPED não for especificado), chamadas subsequentes para funções como WriteFile poderão bloquear a execução do thread de chamada até que ocorra um dos seguintes eventos:

  • A operação de E/S é concluída (neste exemplo, uma gravação de dados).
  • Ocorre um erro de E/S. (Por exemplo, o pipe é fechado da outra extremidade.)
  • Erro na chamada em si (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 desse thread, falhando 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, portanto, os designers de aplicativos devem considerar o uso de E/S assíncrona com objetos de sincronização de thread apropriados, como portas de conclusão de E/S. Para obter mais informações sobre a sincronização de threads, consulte Sobre a sincronização.

Um processo abre um arquivo de 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 E/S síncrona. Quando o arquivo é aberto para E/S assíncrona, um ponteiro para uma estrutura OVERLAPPED é passado para a chamada para ReadFile e WriteFile. Ao executar 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 E/S assíncrona, 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 obter mais informações, consulte https://support.microsoft.com/kb/156932.

 

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 identificador typecast de outros objetos do sistema, como um soquete criado pelo soquete ou funções accept .

Identificadores para objetos de diretório são obtidos chamando a função CreateFile com o atributo FILE_FLAG_BACKUP_SEMANTICS . Os identificadores de diretório quase nunca são usados— os aplicativos de backup são um dos poucos aplicativos que normalmente os usarão.

Depois de abrir o objeto de arquivo para E/S assíncrona, uma estrutura OVERLAPPED deve ser criada, inicializada e passada corretamente para 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 nem 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 para qualquer função ReadFile ou WriteFile que encontrar fora dessa função.

Você também pode criar um evento e colocar o identificador na estrutura OVERLAPPED ; as funções de espera podem ser usadas para aguardar a conclusão da operação de E/S aguardando o identificador de evento.

Conforme mencionado anteriormente, 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ão relatar incorretamente que a operação de E/S foi concluída. Além disso, a função WriteFile às vezes 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 acostumados ao design de E/S síncrona geralmente liberarão recursos de buffer de dados neste momento 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 seja 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 de dupla liberação. Neste exemplo, a recomendação seria permitir que a rotina de porta de conclusão fosse a única responsável por todas as operações de liberação para esses 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, buscando dispositivos), 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 do arquivo para um identificador síncrono é mantida pelo sistema à medida que os dados são lidos ou gravados e também podem ser atualizados 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 extrema cautela. Sempre que uma operação de E/S é iniciada, o sistema operacional define o identificador de arquivo como o estado não atribuído. Cada vez que uma operação de E/S é concluída, o sistema operacional define o identificador de arquivo como o estado sinalizado. Portanto, se um aplicativo iniciar duas operações de E/S e aguardar o identificador de arquivo, não haverá como determinar qual operação será concluída quando o identificador estiver definido como o estado sinalizado. Se um aplicativo precisar executar várias operações de E/S assíncronas em um único arquivo, ele deverá aguardar o identificador de evento na estrutura OVERLAPPED específica para cada operação de E/S, em vez de no identificador de arquivo comum.

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íncronas pendentes.

As funções ReadFileEx e WriteFileEx permitem que um aplicativo especifique uma rotina a ser executada (consulte FileIOCompletionRoutine) quando a solicitação de E/S assíncrona for concluída.