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 para 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
  • Processes
  • Semáforos
  • Threads
  • Temporizadores

Além desses objetos que podem ser esperados, o Windows oferece primitivos de sincronização leves que não usam identificadores de kernel:

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 é ativado 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 tenha sido finalizado.

Outros objetos são úteis na proteção de recursos compartilhados contra o acesso simultâneo. Por exemplo, vários threads podem ter um identificador para um objeto mutex. Antes de acessar um recurso compartilhado, as threads devem chamar uma das funções de espera para aguardar que o estado do mutex seja sinalizado. Quando o mutex é sinalizado, apenas um thread de espera é liberado para acessar o recurso. O estado do mutex é imediatamente redefinido para um estado não sinalizado, de modo que outras threads que estão aguardando permaneçam bloqueadas. Quando a thread terminar de usar o recurso, ela deverá definir o estado do mutex como sinalizado para permitir que outras threads acessem o recurso.

Para os encadeamentos de um único processo, objetos de seções críticas fornecem um meio mais eficiente de sincronização do que mutexes. Uma seção crítica é usada de forma semelhante a um mutex para permitir que apenas um thread por vez use 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 de solicitação será bloqueado. Um thread pode usar a função TryEnterCriticalSection para solicitar a propriedade de uma seção crítica, sem bloquear após a falha na obtenção da seção crítica. Depois de receber a propriedade, o thread será livre para usar o recurso protegido. A execução das outras tarefas do processo não é afetada, a menos que elas 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.