Share via


Sincronização de thread (C# e Visual Basic)

As seções a seguir descrevem os recursos e classes que podem ser usadas para sincronizar o acesso a recursos em aplicativos multithread.

Um dos benefícios do uso de vários threads em um aplicativo é que cada thread é executada de forma assíncrona. Para aplicativos Windows, isso permite que as tarefas demoradas ser executada em segundo plano enquanto a janela do aplicativo e controles permaneçam responsivos. Para o servidor de aplicativos, multithreading fornece a capacidade de lidar com cada solicitação de entrada com um thread diferente. Caso contrário, cada nova solicitação não obter atendida até que a solicitação anterior tivesse sido totalmente satisfeita.

No entanto, a natureza assíncrona de meios de threads que o acesso aos recursos, como identificadores de arquivo, conexões de rede e a memória deve ser coordenada. Caso contrário, dois ou mais threads foi possível acessar o mesmo recurso ao mesmo tempo, cada ciente das ações do outro. O resultado é a corrupção de dados imprevisíveis.

Para as operações simples em tipos de dados numéricos integral, sincronização de threads pode ser realizada com os membros do Interlocked classe. Para todos os outros dados tipos e recursos não seguro para thread, multithreading só podem ser com segurança executados usando as construções neste tópico.

Para obter informações detalhadas sobre a programação multithread, consulte:

O bloqueio e palavras-chave SyncLock

O lock (C#) e SyncLock instruções (Visual Basic) podem ser usadas para garantir que um bloco de código seja concluída sem interrupção por outros threads. Isso é conseguido obter um bloqueio de exclusão mútua para um determinado objeto para a duração do bloco de código.

A lock ou SyncLock instrução é dado um objeto como um argumento e é seguida por um bloco de código que deve ser executado por apenas um thread por vez. Por exemplo:

Public Class TestThreading
    Dim lockThis As New Object 

    Public Sub Process()
        SyncLock lockThis
            ' Access thread-sensitive resources. 
        End SyncLock 
    End Sub 
End Class
public class TestThreading
{
    private System.Object lockThis = new System.Object();

    public void Process()
    {

        lock (lockThis)
        {
            // Access thread-sensitive resources.
        }
    }

}

O argumento fornecido para o lock palavra-chave deve ser um objeto baseado em um tipo de referência e é usado para definir o escopo do bloqueio. No exemplo acima, o escopo de bloqueio está limitado a essa função porque não há referências ao objeto lockThis existe fora da função. Se existisse tal referência, o escopo de bloqueio se estenderia para esse objeto. Estritamente falando, o objeto fornecido é usado unicamente para identificar exclusivamente o recurso que está sendo compartilhado entre vários threads, que ela possa ser uma instância da classe arbitrário. Na prática, no entanto, esse objeto normalmente representa o recurso para o segmento que a sincronização é necessária. Por exemplo, se um objeto de recipiente deve ser usado por vários segmentos, em seguida, o recipiente pode ser passado para bloquear e bloco sincronizado após o bloqueio acessaria o recipiente. Enquanto outros threads bloqueia no mesmo conter antes de acessá-lo, e em seguida, o acesso ao objeto é sincronizado com segurança.

Em geral, é melhor evitar o bloqueio em um public tipo, ou em instâncias de objeto além do controle do seu aplicativo. Por exemplo, lock(this) pode ser problemático se a instância pode ser acessada publicamente, porque o código fora de seu controle, poderá bloquear também o objeto. Isso pode criar situações de bloqueio em que dois ou mais threads aguardar pela liberação do mesmo objeto. O bloqueio em um tipo de dados públicos, em oposição a um objeto, pode causar problemas pelo mesmo motivo. O bloqueio em cadeias de caracteres literais é especialmente perigoso como seqüências de caracteres literais são interned , o common language runtime (CLR). Isso significa que há uma instância de determinada seqüência de caracteres literal para todo o programa, o mesmo objeto exato representa o literal em todos os que executam domínios de aplicativo, em todos os threads. Como resultado, colocar um bloqueio em uma seqüência de caracteres com o mesmo conteúdo em qualquer lugar em que os bloqueios de processo do aplicativo todas as instâncias dessa cadeia de caracteres no aplicativo. Como resultado, é melhor bloquear um membro particular ou protegido que não é interned. Algumas classes fornecem membros especificamente para o bloqueio. O Array tipo, por exemplo, fornece SyncRoot. Muitos tipos de coleção fornecem um SyncRoot também membro.

Para obter mais informações sobre o lock e SyncLock instruções, consulte os seguintes tópicos:

Monitores

Como o lock e SyncLock palavras-chave, monitores impedem blocos de código de execução simultânea por vários segmentos. O Enter método permite que apenas um thread prosseguir para as instruções a seguir; todos os outros threads são bloqueados até que as chamadas do thread em execução Exit. Isso é como usar o lock palavra-chave. Por exemplo:

SyncLock x
    DoSomething()
End SyncLock
lock (x)
{
    DoSomething();
}

Isso é equivalente a:

Dim obj As Object = CType(x, Object)
System.Threading.Monitor.Enter(obj)
Try
    DoSomething()
Finally
    System.Threading.Monitor.Exit(obj)
End Try
System.Object obj = (System.Object)x;
System.Threading.Monitor.Enter(obj);
try
{
    DoSomething();
}
finally
{
    System.Threading.Monitor.Exit(obj);
}

Usando o lock (C#) ou SyncLock palavra-chave de (Visual Basic) é geralmente é preferível usar o Monitor classe diretamente, ambos porque lock ou SyncLock é o mais conciso e porque lock ou SyncLock assegura que o monitor subjacente é liberado, mesmo que o código protegido lança uma exceção. Isso é realizado com o finally palavra-chave, que executa o seu bloco de código associado, independentemente de se uma exceção é lançada.

Eventos de sincronização e identificadores de espera

Usar um monitor ou o bloqueio é útil para evitar a execução simultânea de thread sensíveis blocos de código, mas essas construções não permitir que um segmento para comunicar-se de um evento para outro. Isso requer eventos de sincronização, que são objetos que possuem um dos dois estados, signaled e un-signaled, que pode ser usado para ativar e suspender os threads. Segmentos podem ser suspensa por sendo feitas para esperar um evento de sincronização é sinalizado e podem ser ativados por alterando o estado do evento sinalizado. Se um segmento tenta aguardar um evento que já está sinalizado, o segmento continua a executar sem atraso.

Há dois tipos de eventos de sincronização: AutoResetEvent, e ManualResetEvent. Diferem apenas no que AutoResetEvent alterações de sinalizado para não sinalizado automaticamente sempre que ele ativa um thread. Por outro lado, um ManualResetEvent permite que qualquer número de threads para ser ativado por seu estado sinalizado e só será revertido para um sinalizado estado quando seu Reset método é chamado.

Threads podem ser feitas para esperar por eventos por uma chamada dos métodos de espera, tal como WaitOne, WaitAny, ou WaitAll. WaitHandle.WaitOnefaz com que o thread a esperar até que um único evento torna-se sinalizado, WaitHandle.WaitAny bloqueia um thread até que um ou mais eventos indicados sinalizados, e WaitHandle.WaitAll bloqueia o segmento até que todos os eventos indicados fique sinalizados. Um evento se torna sinalizado quando seu Set método é chamado.

No exemplo a seguir, um segmento é criado e iniciado pela Main função. O novo thread aguarda um evento usando o WaitOne método. O thread está suspenso até que o evento torna-se sinalizado pelo thread principal que está executando o Main função. Depois que o evento torna-se sinalizado, retorna o thread auxiliar. Nesse caso, porque o evento só é usado para ativação de um segmento, ou o AutoResetEvent ou ManualResetEvent classes poderiam ser usadas.

Imports System.Threading

Module Module1
    Dim autoEvent As AutoResetEvent

    Sub DoWork()
        Console.WriteLine("   worker thread started, now waiting on event...")
        autoEvent.WaitOne()
        Console.WriteLine("   worker thread reactivated, now exiting...")
    End Sub 

    Sub Main()
        autoEvent = New AutoResetEvent(False)

        Console.WriteLine("main thread starting worker thread...")
        Dim t As New Thread(AddressOf DoWork)
        t.Start()

        Console.WriteLine("main thread sleeping for 1 second...")
        Thread.Sleep(1000)

        Console.WriteLine("main thread signaling worker thread...")
        autoEvent.Set()
    End Sub 
End Module
using System;
using System.Threading;

class ThreadingExample
{
    static AutoResetEvent autoEvent;

    static void DoWork()
    {
        Console.WriteLine("   worker thread started, now waiting on event...");
        autoEvent.WaitOne();
        Console.WriteLine("   worker thread reactivated, now exiting...");
    }

    static void Main()
    {
        autoEvent = new AutoResetEvent(false);

        Console.WriteLine("main thread starting worker thread...");
        Thread t = new Thread(DoWork);
        t.Start();

        Console.WriteLine("main thread sleeping for 1 second...");
        Thread.Sleep(1000);

        Console.WriteLine("main thread signaling worker thread...");
        autoEvent.Set();
    }
}

Objeto mutex

A mutex é semelhante a um monitor; Ele impede a execução simultânea de um bloco de código por mais de um segmento por vez. Na verdade, o nome "mutex" é uma forma abreviada do termo "mutuamente excludentes". Ao contrário de monitores, no entanto, um mutex pode ser usado para sincronizar segmentos entre processos. Um mutex é representado pela Mutex classe.

Quando usado para sincronização entre processos, um mutex é chamado um mutex denominado porque está a ser usado em outro aplicativo, e portanto não pode ser compartilhado por meio de uma variável global ou estática. Ele deve ser fornecido um nome para que ambos os aplicativos possam acessar o mesmo objeto mutex.

Embora um mutex pode ser usado para sincronização de thread direcionado, usando Monitor é geralmente preferido, porque os monitores foram projetados especificamente para o.NET Framework e, portanto, fazer melhor uso dos recursos. Em contraste, o Mutex classe é um wrapper para uma construção do Win32. Embora seja mais eficiente do que um monitor, um mutex requer transições de interoperabilidade que são mais dispendiosas que aquelas necessárias a Monitor classe. Para obter um exemplo do uso de um mutex, consulte Mutexes.

Classe Interlocked

Você pode usar os métodos da classe Interlocked para evitar problemas que podem ocorrer quando vários segmentos tentarem simultaneamente atualizar ou comparar o mesmo valor. Os métodos dessa classe permitem o incremento, diminuição, troca e comparação de valores de qualquer segmento seguro.

Bloqueios ReaderWriter

Em alguns casos, você pode querer bloquear um recurso somente quando os dados estiverem sendo escritos e permitir que vários clientes simultaneamente leiam os dados quando estes não estiverem sendo atualizados. A classe ReaderWriterLock garante o acesso exclusivo a um recurso enquanto um segmento está modificando o recurso, mas ele permite acesso não exclusivo ao ler o recurso. Bloqueios ReaderWriter são uma alternativa útil para bloqueios exclusivos que causam outros segmentos para esperar, mesmo quando os segmentos não precisam atualizar dados.

Deadlocks

Sincronização de segmentos são incalculáveis em aplicativos multissegmentados, mas há sempre o perigo de criar um deadlock, onde vários segmentos estão aguardando um pelo outro e o aplicativo chega a uma interrupção. Um deadlock é análogo a uma situação na qual carros são estacionados em uma parada de quatro vias e cada pessoa está aguardando a outra para ir. Evitar deadlocks é importante; a chave é um planejamento cuidadoso. Geralmente você pode prever situações de bloqueio por diagramação de aplicativos multissegmentados antes de iniciar a codificação.

Seções relacionadas

Como usar um pool de thread (C# e Visual Basic)

COMO: Sincronizar o acesso a um recurso compartilhado em um ambiente multithread usando o Visual C#.NET

COMO: Criar um segmento usando Visual C#.NET

COMO: Enviar um Item de trabalho para o Pool de segmentos usando o Visual C#.NET

COMO: Sincronizar o acesso a um recurso compartilhado em um ambiente multithread usando o Visual C#.NET

Consulte também

Referência

Instrução SyncLock

Instrução lock (Referência de C#)

Thread

WaitOne

WaitAny

WaitAll

Join

Start

Sleep

Monitor

Mutex

AutoResetEvent

ManualResetEvent

Interlocked

WaitHandle

EventWaitHandle

System.Threading

Set

Conceitos

Aplicativos multithread (C# e Visual Basic)

Mutexes

Monitores

Operações interconectadas

AutoResetEvent

Sincronizando dados para multithreading

Outros recursos

Implementando o modelo de programação assíncrona do CLR

simplificado APM com C#

monitor de deadlocks

Multithreading em componentes

HOW TO: Synchronize Access to a Shared Resource in a Multithreading Environment by Usando o Visual C#.NET