Pergunta

I foi depuração uma aplicação multi-threaded e encontrado a estrutura interna de CRITICAL_SECTION. Achei membro de dados LockSemaphore de CRITICAL_SECTION interessante.

Parece que LockSemaphore é um evento auto-reset (não um semáforo como o nome sugere) e sistema operacional cria este evento silenciosamente quando a primeira vez que um thread espera no Critcal Section que está bloqueado por algum outro segmento.

Agora, eu estou querendo saber é seção crítica sempre mais rápido? Evento é um objeto de kernel e cada objeto de seção crítica é associado com objeto de evento então como Critical Section pode ser mais rápido em comparação a outros objetos do kernel como Mutex? Além disso, como é objeto de evento interno realmente afeta o desempenho da seção crítica?

Aqui está a estrutura do CRITICAL_SECTION:

struct RTL_CRITICAL_SECTION
{
    PRTL_CRITICAL_SECTION_DEBUG DebugInfo;
    LONG LockCount;
    LONG RecursionCount;
    HANDLE OwningThread;
    HANDLE LockSemaphore;
    ULONG_PTR SpinCount;
};
Foi útil?

Solução

Quando eles dizem que uma seção crítica é "rápido", que significam "É barato para adquirir um, quando ele já não estiver bloqueado por outro thread".

[Nota que, se é já bloqueado por outro segmento, então não importa quase tanto quão rápido ele é.]

A razão pela qual ele é rápido é porque, antes de entrar no kernel, ele usa o equivalente a InterlockedIncrement em um desses campo LONG (talvez no campo LockCount) e se for bem sucedido, então ele considera o bloqueio adquirido sem ter passado no kernel.

A API InterlockedIncrement é que eu acho implementado no modo de usuário como um "LOCK INC" opcode ... em outras palavras, você pode adquirir uma seção crítica incontestável, sem fazer qualquer transição anel no kernel em tudo.

Outras dicas

No trabalho performance, algumas coisas cair na "sempre" categoria :) Se você implementar algo a si mesmo que é semelhante a uma seção crítica OS usando outras primitivas, em seguida, as probabilidades são de que será mais lento na maioria dos casos.

A melhor maneira de responder a sua pergunta é com medições de desempenho. Como objetos OS executar é muito dependente do cenário. Por exemplo, as seções críticas são gerais considerado 'rápido' se a contenção é baixa. Eles também são considerados rápido se o tempo de bloqueio é menor que o tempo de contagem de rotação.

A coisa mais importante para determinar é se disputa em uma seção crítica é a primeira ordem fator limitante na sua aplicação. Se não, então basta usar uma seção crítica normaly e trabalhar em suas aplicações gargalo primário (ou pescoço).

Se o desempenho seção crítica é crítica, então você pode considerar o seguinte.

  1. Com cuidado, coloque a contagem de bloqueio de rotação para as suas seções críticas 'quentes'. Se o desempenho é fundamental, então o trabalho aqui é pena. Lembre-se, enquanto o bloqueio de rotação faz evitar o modo de usuário para transição kernel, que consome tempo de CPU em uma taxa furiosa - enquanto fiação, nada mais consegue usar esse tempo da CPU. Se um bloqueio é mantido por tempo suficiente, então o segmento de fiação irá bloquear real, liberando que CPU para fazer outro trabalho.
  2. Se você tem um padrão de leitor / gravador, em seguida, considere o uso do Magro leitor / gravador (SRW) fechaduras . A desvantagem é que eles só estão disponíveis no Vista e Windows Server 2008 e produtos posteriores.
  3. Você pode ser capaz de usar variáveis ??de condição com a sua seção crítica para minimizar votação e contenção, acordando tópicos somente quando necessário. Novamente, estes são suportados no Windows Vista e Windows Server 2008 e produtos posteriores.
  4. bloqueadas Listas vinculada isoladamente ( SLIST) - estes são eficientes e 'Lock Free'. Ainda melhor, eles são suportados em XP e Windows Server 2003 e produtos posteriores.
  5. Examine seu código -. Você pode ser capaz de romper um bloqueio 'quente' por refatoração algum código e usando uma operação encravamento ou SLIST para sincronização e comunicação

Em resumo - cenários de ajuste que têm contenção de bloqueio pode ser um desafio (mas interessante!) Trabalho. Concentre-se em medir o seu desempenho das aplicações e entender onde seus caminhos quentes são. As ferramentas Xperf na Ferramenta Desempenho do Windows kit é seu amigo aqui :) Acabamos de lançar a versão 4.5 do Microsoft Windows SDK para Windows 7 e .NET Framework 3.5 SP1 ( ISO é aqui , instalador web aqui ). Você pode encontrar o fórum para as ferramentas Xperf aqui . V4.5 suporta totalmente Win7, Vista, Windows Server 2008 -. Todas as versões

CriticalSections é mais rápido, mas InterlockedIncrement / InterlockedDecrement é mais. Veja este exemplo de uso implementação LightweightLock completa cópia .

Os CriticalSections vai girar um curto espaço de tempo (alguns ms) e manter a verificação se o bloqueio é gratuito. Após a contagem de spin 'vezes fora', ele irá, em seguida, cair de volta para o evento kernel. Assim, no caso em que o titular do bloqueio sai rapidamente, você nunca tem que fazer a transição caro para o código do kernel.

EDIT: Fui e encontrei alguns comentários no meu código: aparentemente, o MS Heap Manager usa uma contagem de rotação de 4000 (incrementos inteiros, não ms)

Aqui está uma maneira de olhar para ele:

Se não houver contenção, então o bloqueio de giro é muito rápido comparado a ir para o modo kernel para um Mutex.

Quando houver contenção, um CriticalSection é ligeiramente mais caro do que usar um Mutex diretamente (por causa do trabalho extra para detectar o estado spinlock).

Então, tudo se resume a uma média ponderada, onde os pesos depender das especificidades do seu padrão de chamada. Dito isto, se você tem pouca disputa, em seguida, um CriticalSection é grande vitória. Se, por outro lado, você tem consistentemente muita disputa, então você estaria pagando uma pequena penalidade sobre usando um Mutex diretamente. Mas, nesse caso, o que você iria ganhar com a mudança para um Mutex é pequeno, então você provavelmente seria melhor para tentar reduzir a contenção.

seção crítica é mais rápido do que mutex porque porque secção crítica não é um objeto de kernel. Isso faz parte da memória global do processo atual. Mutex realmente reside no Kernel e criação de objeto mutext requer um interruptor do kernel, mas em caso de seção crítica não. Apesar de seção crítica é rápido, haverá um interruptor do kernel enquanto utiliza seção crítica quando tópicos vão esperar Estado. Isso ocorre porque fio programação acontece no lado kernel.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top