Objetos de seção crítica
Um objeto de seção crítica fornece sincronização semelhante à fornecida por um objeto mutex, exceto que uma seção crítica só pode ser usada pelos threads de um único processo. Objetos de seção críticos não podem ser compartilhados entre processos.
Objetos event, mutex e semaphore também podem ser usados em um aplicativo de processo único, mas objetos de seção críticos fornecem um mecanismo um pouco mais rápido e eficiente para sincronização de exclusão mútua (um teste específico do processador e instrução de definição). Como um objeto mutex, um objeto de seção crítica pode pertencer a apenas um thread por vez, o que o torna útil para proteger um recurso compartilhado contra acesso simultâneo. Ao contrário de um objeto mutex, não há como dizer se uma seção crítica foi abandonada.
A partir do Windows Server 2003 com o Service Pack 1 (SP1), os threads que aguardam em uma seção crítica não adquirem a seção crítica por 1 a 0. Essa alteração aumenta significativamente o desempenho para a maioria dos códigos. No entanto, alguns aplicativos dependem da ordenação FIFO (primeiro a entrar e sair) e podem ter um desempenho ruim ou não nas versões atuais do Windows (por exemplo, aplicativos que têm usado seções críticas como um limitador de taxa). Para garantir que o código continue funcionando corretamente, talvez seja necessário adicionar um nível adicional de sincronização. Por exemplo, suponha que você tenha um thread de produtor e um thread de consumidor que estão usando um objeto de seção crítico para sincronizar seu trabalho. Crie dois objetos de evento, um para cada thread usar para sinalizar que ele está pronto para o outro thread continuar. O thread do consumidor aguardará que o produtor sinalize seu evento antes de entrar na seção crítica, e o thread do produtor aguardará o thread do consumidor sinalizar seu evento antes de entrar na seção crítica. Depois que cada thread sai da seção crítica, ele sinaliza seu evento para liberar o outro thread.
Windows Server 2003 e Windows XP: Os threads que estão aguardando uma seção crítica são adicionados a uma fila de espera; eles são acordados e geralmente adquirem a seção crítica na ordem em que foram adicionados à fila. No entanto, se os threads forem adicionados a essa fila a uma taxa rápida o suficiente, o desempenho poderá ser degradado devido ao tempo necessário para despertar cada thread em espera.
O processo é responsável por alocar a memória usada por uma seção crítica. Normalmente, isso é feito simplesmente declarando uma variável do tipo CRITICAL_SECTION. Antes que os threads do processo possam usá-lo, inicialize a seção crítica usando a função InitializeCriticalSection ou InitializeCriticalSectionAndSpinCount .
Um thread usa a função EnterCriticalSection ou TryEnterCriticalSection para solicitar a propriedade de uma seção crítica. Ele usa a função LeaveCriticalSection para liberar a propriedade de uma seção crítica. Se o objeto de seção crítica pertencer atualmente a outro thread, EnterCriticalSection aguardará indefinidamente a propriedade. Por outro lado, quando um objeto mutex é usado para exclusão mútua, as funções de espera aceitam um intervalo de tempo limite especificado. A função TryEnterCriticalSection tenta inserir uma seção crítica sem bloquear o thread de chamada.
Quando um thread possui uma seção crítica, ele pode fazer chamadas adicionais para EnterCriticalSection ou TryEnterCriticalSection sem bloquear sua execução. Isso impede que um thread seja deadlock enquanto aguarda uma seção crítica que ele já possui. Para liberar sua propriedade, o thread deve chamar LeaveCriticalSection uma vez para cada vez que ele entrou na seção crítica. Não há nenhuma garantia sobre a ordem em que os threads em espera adquirirão a propriedade da seção crítica.
Um thread usa a função InitializeCriticalSectionAndSpinCount ou SetCriticalSectionSpinCount para especificar uma contagem de rotação para o objeto de seção crítica. Girar significa que, quando um thread tenta adquirir uma seção crítica que está bloqueada, o thread entra em um loop, verifica se o bloqueio é liberado e, se o bloqueio não é liberado, o thread entra em suspensão. Em sistemas de processador único, a contagem de rotação é ignorada e a contagem de rotação de seção crítica é definida como 0 (zero). Em sistemas multiprocessadores, se a seção crítica não estiver disponível, o thread de chamada girará dwSpinCount vezes antes de executar uma operação de espera em um semáforo associado à seção crítica. Se a seção crítica ficar livre durante a operação de rotação, o thread de chamada evitará a operação de espera.
Qualquer thread do processo pode usar a função DeleteCriticalSection para liberar os recursos do sistema alocados quando o objeto de seção crítica é inicializado. Depois que essa função é chamada, o objeto de seção crítica não pode ser usado para sincronização.
Quando um objeto de seção crítica pertence, os únicos outros threads afetados são os threads que estão aguardando a propriedade em uma chamada para EnterCriticalSection. Os threads que não estão aguardando são gratuitos para continuar em execução.
Tópicos relacionados