Compartilhar via


Gerenciado Threading práticas recomendadas 

Multithreading requer programação cuidadosa.Para a maioria das tarefas, você pode reduzir a complexidade enfileirando solicitações para execução por threads de um pool.Este tópico aborda situações mais difíceis, como coordenar o trabalho de várias threads, ou manipular threads que gerem bloqueios.

Bloqueios e condições Race

Multithreading resolve problemas com taxa de transferência e responsividade, mas ela introduz novos problemas ao fazer isso: deadlocks e condições de corrida.

Deadlocks

Um deadlock ocorre quando cada uma das dois threads tenta bloquear um recurso já bloquada pela outra.Nenhuma das duas threads consegue seguir adiante.

Muitos métodos das classes de segmentação gerenciados fornecem tempos limite para ajudá-lo detectar bloqueios.Por exemplo, o código a seguir tenta se conseguir um bloqueio na instância atual.Se o bloquear não é obtido em 300 milissegundos, Monitor.TryEnter Retorna False.

If Monitor.TryEnter(Me, 300) Then
    Try
        ' Place code protected by the Monitor here.
    Finally
        Monitor.Exit(Me)
    End Try
Else
    ' Code to execute if the attempt times out.
End If
if (Monitor.TryEnter(this, 300)) {
    try {
        // Place code protected by the Monitor here.
    }
    finally {
        Monitor.Exit(this);
    }
}
else {
    // Code to execute if the attempt times out.
}

Condições de corrida

Uma condição de corrida é um erro que ocorre quando o resultado de um programa depende do qual dos dois ou mais segmentos atinge um bloco de código específico primeiro.Executar o programa muitas vezes produz resultados diferentes, e o resultado de qualquer executar determinada não pode ser previsto.

Um exemplo simples de uma condição de corrida é incrementando um campo.Suponha que uma classe tem uma particular estático campo (Compartilhado no Visual Basic) que é incrementado toda vez que uma instância da classe é criada, usando um código sistema autônomo objCt++; (Translation from VPE for Csharp) ou objCt += 1 (Visual Basic). Shared ) que é incrementado sempre que uma instância da classe é criada, usando código como objCt++; (C#) no Visual Basic Esta operação requer carregar o valor objCt em um registrador, incrementando o valor, e armazená-lo no objCt.

Em um aplicativo com vários segmentos, um segmento que tenha carregado e incrementado o valor pode ser apropriado por outro segmento que executa todas as etapas três; quando o primeiro segmento reinicia a execução e armazena o valor, ele substitui objCt Sem levando em conta o fato de que o valor foi alterado nesse ínterim.

Essa condição de corrida específica é com com facilidade evitada usando métodos do Interlocked classe, sistema autônomo Interlocked.Increment. Para saber mais sobre outras técnicas para sincronizar dados entre vários threads, consulte Sincronizando dados para o multithreading.

Condições de corrida também podem ocorrer quando você sincronizar as atividades de vários segmentos.Sempre que você escrever uma linha de código, você deve considerar o que poderia acontecer se um segmento foram apropriado antes de executar a linha (ou antes de qualquer das instruções da máquina individuais que compõem a linha), e outro segmento overtook-lo.

Número de processadores

Multithreading resolve problemas diferentes para os computadores processador único-que executar a maioria dos software usuário final, e os computadores multiprocessador normalmente usados como servidores.

Computadores Processor único

Multithreading fornece receptividade maior para o usuário de computador, e usa tempo ocioso para tarefas em segundo plano.Se você usar multithreading em um computador com um único processador:

  • Apenas um segmento é executada em qualquer instante.

  • Um segmento em segundo plano executa somente quando o segmento do usuário principal estiver ocioso.Um segmento de primeiro plano que executa constantemente starves segmentos Plano de de fundo de tempo do processador.

  • Quando você telefonar o Thread.Start método em um segmento, esse segmento não é iniciado até que o thread corrente produz ou está em execução superado pelo sistema operacional.

  • Condições de corrida geralmente ocorrem porque o programador não antecipou o fato que um segmento pode ser apropriado em um momento estranho, às vezes permitindo outro segmento para alcançar um bloco de códigos primeiro.

Computadores multiprocessadores

Multithreading fornece maior taxa de transferência.Processadores dez podem fazer o trabalho de um, dez vezes mas somente se o trabalho é dividido para que todos os dez pode estar trabalhando uma vez; segmentos fornecer uma maneira fácil de divida o trabalho e explorar o poder de processamento extra.Se você usar multithreading em um computador multiprocessador:

  • O número de segmentos que podem executar simultaneamente é limitado pelo número de processadores.

  • Um segmento em segundo plano executa somente quando o número de segmentos de primeiro plano executar é menor que o número de processadores.

  • Quando você liga o Thread.Start método em um thread, que segmento pode ou não pode ser iniciado executando imediatamente, dependendo no número de processadores e o número de threads aguardando para executar.

  • Condições de corrida podem ocorrer porque não apenas segmentos são apropriados inesperadamente, mas porque dois segmentos executando em diferentes processadores podem ser racing para alcançar o mesmo bloco de código.

Membros estático e construtores estático

Uma classe não foi inicializada até seu construtor de classe (static construtor no translation from VPE for Csharp, Shared Sub New no Visual Basic) concluiu a execução. Para impedir a execução de código em um tipo que não é inicializado, os blocos de tempo de execução comum linguagem todas as chamadas de outros segmentos para static membros do (a classeShared membros no Visual Basic) até o construtor da classe tenha terminado sua execução.

Por exemplo, se um construtor de classe inicia um novo thread e o procedimento de thread chama um static membro da classe, novos blocos de thread até que o construtor da classe seja concluída.

Isso se aplica a qualquer tipo que pode ter um static construtor.

Recomendações gerais

Considere as seguintes diretrizes ao usando vários encadeamentos:

  • Não use Thread.Abort Para terminar outros threads. Chamada Abort em outro segmento é akin para lançar uma exceção em que segmento, sem saber que aponte esse segmento tiver alcançado no seu processamento.

  • Não use Thread.Suspend e Thread.Resume Para sincronizar as atividades de vários segmentos. Usar Mutex, ManualResetEvent, AutoResetEvent, e Monitor.

  • Não controlar a execução de segmentos de trabalho do seu programa principal (usando eventos, por exemplo).Em vez disso, criar seu programa de modo que segmentos de trabalho são responsáveis por aguardando até que trabalho esteja disponível, executando-la, e notificar outras partes do seu programa quando terminar.Não use Thread.Suspend e Thread.Resume Para sincronizar as atividades de vários segmentos.Monitor.PulseAll é útil em situações onde o trabalho threads bloco.

  • Não use tipos sistema autônomo bloquear de objetos.Isto é, evite códigos, sistema autônomo lock(typeof(X)) em translation from VPE for Csharp ou SyncLock(GetType(X)) no Visual Basic ou o uso de Monitor.Enter com Type objetos. Para um determinado tipo, há apenas uma instância de System.Type por domínio do aplicativo. Se o tipo de que usar um bloquear for público, o código diferente de seu próprio pode demorar bloqueios, entrelinhamento a deadlocks.Para obter questões adicionais, consulte Práticas recomendadas de confiabilidade.

  • Tenha cuidado ao bloqueio de instâncias, por exemplo, lock(this) em translation from VPE for Csharp ou SyncLock(Me) no Visual Basic. Se Outros código no seu aplicativo, externo tipo, usa um bloquear no objeto, os deadlocks poderiam ocorrer.

  • Garantir um thread que entrou em um monitor sempre deixa esse monitor, mesmo que uma exceção ocorre enquanto o thread está no monitor.The C# bloquear demonstrativo e o Visual Basic SyncLock demonstrativo fornecem esse comportamento automaticamente, empregando um Por fim bloco para garantir que Monitor.Exit é chamado. Se você não pode garantir que Exit será chamado, considere alterar o design para usar Mutex.Um mutex automaticamente é liberado quando termina o segmento que atualmente possui ele.

  • Usar vários segmentos para tarefas que requerem recursos diferentes, e evite atribuir vários segmentos a um único recurso.Por exemplo, qualquer tarefa que envolva E/s beneficia de ter seu próprio segmento, porque esse segmento será bloquear durante operações de E/s e assim permitir que outros segmentos para executar.Entrada do usuário é outro recurso que beneficia de um segmento dedicado.Em um computador com um único processador, uma tarefa que envolva cálculo intenso coexists com entrada do usuário e com tarefas que envolvam E/s, mas várias tarefas muito computation-contend com os outros.

  • Considere o uso de métodos do Interlocked classe para alterações de estado simples, em vez de usar o lock demonstrativo)SyncLock no Visual Basic). The lock demonstrativo é uma mercadoria ferramenta de uso geral, mas o Interlocked classe fornece melhor desempenho para atualizações que devem ser atômica. Internamente, ele executa um prefixo de bloqueio único se não houver nenhum conflito.Em análises de código assista para código como essa mostrado nos exemplos a seguir.No primeiro exemplo, uma variável de estado é incrementado:

    SyncLock lockObject
        myField += 1
    End SyncLock
    
    lock(lockObject) 
    {
        myField++;
    }
    

    Você pode melhorar o desempenho usando o Increment método em vez da lock demonstrativo, sistema autônomo segue:

    System.Threading.Interlocked.Increment(myField)
    
    System.Threading.Interlocked.Increment(myField);
    
    Observação:

    No .NET estrutura versão 2,0, o Add método fornece atualizações atômicas em incrementos maiores que 1.

    No segundo exemplo, uma variável de tipo de referência é atualizada apenas se for uma referência nula (Nothing no Visual Basic).

    If x Is Nothing Then
        SyncLock lockObject
            If x Is Nothing Then
                x = y
            End If
        End SyncLock
    End If
    
    if (x == null)
    {
        lock (lockObject)
        {
            if (x == null)
            {
                x = y;
            }
        }
    }
    

    Usando o o desempenho pode ser melhoradoCompareExchange método em vez disso, da seguinte maneira:

    System.Threading.Interlocked.CompareExchange(x, y, Nothing)
    
    System.Threading.Interlocked.CompareExchange(ref x, y, null);
    
    Observação:

    No .NET estrutura versão 2,0, o CompareExchange método tem uma sobrecarga genérica que pode ser usada para a substituição de segurança de tipos de qualquer tipo de referência.

Recomendações para bibliotecas de classe

Considerar as seguintes diretrizes ao criar bibliotecas de classes para multithreading:

  • Evitar a necessidade de sincronização, se possível.Isso é especialmente verdadeiro para código muito usado.Por exemplo, um algoritmo pode ser ajustado para tolerate uma condição de corrida em vez de eliminá-lo.Sincronização desnecessária diminui o desempenho e cria a possibilidade de bloqueios e condições de corrida.

  • Tornar os dados estático (Shared no Visual Basic) thread seguro por padrão.

  • Não faça segmento de dados da instância seguro por padrão.Adicionar bloqueios para criar código isenta de segmentos diminui o desempenho, aumenta contenção de bloqueio, e cria a possibilidade de bloqueios para ocorrer.Em comum modelos do aplicativo, apenas um segmento por vez executa código do usuário, que reduz a necessidade de segurança do segmento.Por esse motivo, as bibliotecas de classes .NET Framework são não segmento seguro por padrão.

  • Evite fornecer métodos estáticos que alterar estado estático.Nos cenários comuns, do servidor estado estático é compartilhado entre solicitações, que significa vários segmentos podem executar esse código ao mesmo tempo.Isso abrirá a possibilidade de segmentação bugs Backup.Considere usar um padrão de design que encapsula dados em instâncias que não são compartilhadas nas solicitações.Além disso, se dados estáticos estiverem sincronizados, chamadas entre métodos estáticos que alterar estado podem resultar em bloqueios ou redundante sincronização, afetar negativamente o desempenho.

Consulte também

Conceitos

Segmentos e Threading

Outros recursos

Threads gerenciadas