Sincronizando a execução de vários threads
Para evitar condições de corrida e deadlocks, é necessário sincronizar o acesso por vários threads com recursos compartilhados. A sincronização também é necessária para garantir que o código interdependente seja executado na sequência adequada.
Há vários objetos cujos identificadores podem ser usados para sincronizar vários threads. Esses objetos incluem:
- Buffers de entrada do console
- Eventos
- Mutexes
- Processos
- Semáforos
- Threads
- Temporizadores
O estado de cada um desses objetos é sinalizado ou não sinalizado. Quando você especifica um identificador para qualquer um desses objetos em uma chamada para uma das funções de espera, a execução do thread de chamada é bloqueada até que o estado do objeto especificado seja sinalizado.
Alguns desses objetos são úteis para bloquear um thread até que algum evento ocorra. Por exemplo, um identificador de buffer de entrada do console é sinalizado quando há entrada não lida, como um pressionamento de tecla ou clique no botão do mouse. Os identificadores de processo e de thread são sinalizados quando o processo ou o thread é encerrado. Isso permite que um processo, por exemplo, crie um processo filho e bloqueie sua própria execução até que o novo processo seja encerrado.
Outros objetos são úteis na proteção de recursos compartilhados contra acesso simultâneo. Por exemplo, vários threads podem ter um identificador para um objeto mutex. Antes de acessar um recurso compartilhado, os threads devem chamar uma das funções de espera para aguardar o estado do mutex ser sinalizado. Quando o mutex é sinalizado, apenas um thread de espera é liberado para acessar o recurso. O estado do mutex é imediatamente redefinido para não sinalizado para que quaisquer outros threads de espera permaneçam bloqueados. Quando o thread for concluído com o recurso, ele deverá definir o estado do mutex como sinalizado para permitir que outros threads acessem o recurso.
Para os threads de um único processo, os objetos de seção crítica fornecem um meio mais eficiente de sincronização do que os mutexes. Uma seção crítica é usada como um mutex para habilitar um thread de cada vez para usar o recurso protegido. Um thread pode usar a função EnterCriticalSection para solicitar a propriedade de uma seção crítica. Se ele já pertencer a outro thread, o thread solicitante será bloqueado. Um thread pode usar a função TryEnterCriticalSection para solicitar a propriedade de uma seção crítica, sem bloquear a falha ao obter a seção crítica. Depois que ele recebe a propriedade, o thread é livre para usar o recurso protegido. A execução dos outros threads do processo não é afetada, a menos que eles tentem entrar na mesma seção crítica.
A função WaitForInputIdle faz com que um thread aguarde até que um processo especificado seja inicializado e aguardando a entrada do usuário sem nenhuma entrada pendente. Chamar WaitForInputIdle pode ser útil para sincronizar processos pai e filho, pois CreateProcess retorna sem esperar que o processo filho conclua sua inicialização.
Para obter mais informações, consulte Sincronização.