Monitores

Monitorobjetos expõem a capacidade de sincronizar o acesso a uma região de código utilizando e liberar um bloqueio em um objeto específico usando o Monitor.Enter, Monitor.TryEnter, e Monitor.Exitmétodos. Depois de ter um bloqueio em uma região de código, você pode usar o Monitor.Wait, Monitor.Pulse, e Monitor.PulseAllmétodos. Waitlibera o bloqueio se ele é mantido e aguarda para ser notificado. Quando Wait é notificado, ele retorna e obtém o bloqueio novamente. Ambos Pulse ePulseAll sinal para o próximo segmento na fila de espera para continuar.

O Visual Basic SyncLock e C# lock a usar instruções Monitor.Enter assuma o bloqueio e Monitor.Exit para liberar o proprietário. A vantagem de usar as instruções de linguagem é que tudo na lock ou SyncLock bloco está incluído em um Try instrução. O Try instrução possui um Finally bloco para garantir que o bloqueio será liberado.

Monitorbloqueia os objetos (ou seja, tipos de referência), não os tipos de valor. Enquanto você pode passar um tipo de valor para Enter e Exit, for in a box separadamente para cada chamada. Desde que cada chamada cria um objeto separado, Enter nunca blocos e o código que ele está protegendo supostamente não está realmente sincronizado. Além disso, o objeto é passado para Exit é diferente do objeto passado para Enter, portanto, Monitor lança SynchronizationLockException com a mensagem "método de sincronização do objeto foi chamado a partir de um bloco não sincronizado de código." O exemplo a seguir ilustra esses problemas.

Try
    Dim x As Integer = 1
    ' The call to Enter() creates a generic synchronizing object for the value
    ' of x each time the code is executed, so that Enter never blocks.
    Monitor.Enter(x)
    Try
        ' Code that needs to be protected by the monitor.
    Finally
        ' Always use Finally to ensure that you exit the Monitor.

        ' The call to Exit() will FAIL!!!
        ' The synchronizing object created for x in Exit() will be different
        ' than the object used in Enter(). SynchronizationLockException
        ' will be thrown.
        Monitor.Exit(x)
    End Try
Catch SyncEx As SynchronizationLockException
    Console.WriteLine("A SynchronizationLockException occurred. Message:")
    Console.WriteLine(SyncEx.Message)
End Try
try
{
    int x = 1;
    // The call to Enter() creates a generic synchronizing object for the value
    // of x each time the code is executed, so that Enter never blocks.
    Monitor.Enter(x);
    try
    {
        // Code that needs to be protected by the monitor.
    }
    finally
    {
        // Always use Finally to ensure that you exit the Monitor.

        // The call to Exit() will FAIL!!!
        // The synchronizing object created for x in Exit() will be different
        // than the object used in Enter(). SynchronizationLockException
        // will be thrown.
        Monitor.Exit(x);
    }
}
catch (SynchronizationLockException SyncEx)
{
    Console.WriteLine("A SynchronizationLockException occurred. Message:");
    Console.WriteLine(SyncEx.Message);
}
try
{
    int x = 1;
    // The call to Enter() creates a generic synchronizing object for the value
    // of x each time the code is executed, so that Enter never blocks.
    Monitor::Enter(x);
    try
    {
        // Code that needs to be protected by the monitor.
    }
    finally
    {
        // Always use Finally to ensure that you exit the Monitor.

        // The call to Exit() will FAIL!!!
        // The synchronizing object created for x in Exit() will be different
        // than the object used in Enter(). SynchronizationLockException
        // will be thrown.
        Monitor::Exit(x);
    }
}
catch (SynchronizationLockException^ SyncEx)
{
    Console::WriteLine("A SynchronizationLockException occurred. Message:");
    Console::WriteLine(SyncEx->Message);
}

Embora a caixa de uma variável do tipo de valor antes de chamar Enter e Exit, conforme mostrado no seguinte exemplo e passar o mesmo objeto in a box para ambos os métodos, não há nenhuma vantagem para fazer isso. As alterações a variável não são refletidas na cópia in a box, e não é possível alterar o valor da cópia in a box.

Dim x As Integer = 1
Dim o As object = x

Monitor.Enter(o)
Try
    ' Code that needs to be protected by the monitor.
Finally
    ' Always use Finally to ensure that you exit the Monitor.
    Monitor.Exit(o)
End Try
int x = 1;
object o = x;

Monitor.Enter(o);
try
{
    // Code that needs to be protected by the monitor.
}
finally
{
    // Always use Finally to ensure that you exit the Monitor.
    Monitor.Exit(o);
}
int x = 1;
Object^ o = x;

Monitor::Enter(o);
try
{
    // Code that needs to be protected by the monitor.
}
finally
{
    // Always use Finally to ensure that you exit the Monitor.
    Monitor::Exit(o);
}

É importante observar a distinção entre o uso de Monitor e WaitHandle objetos. Monitorobjetos são puramente gerenciado, totalmente portátil e podem ser mais eficientes em termos de requisitos de recursos de sistema operacional. WaitHandleobjetos representam objetos waitable do sistema operacional, são úteis para a sincronização entre código gerenciado e e exponham a alguns recursos avançados do sistema operacional, como a capacidade de se esperar muitos objetos ao mesmo tempo.

O exemplo de código a seguir demonstra o uso combinado da Monitor classe (implementada com o lock e SyncLock instruções do compilador), o Interlocked classe e o AutoResetEvent classe.

Imports System
Imports System.Threading

' Note: The class whose internal public member is the synchronizing
' method is not public; none of the client code takes a lock on the
' Resource object.The member of the nonpublic class takes the lock on
' itself. Written this way, malicious code cannot take a lock on
' a public object.
Class SyncResource
    Public Sub Access(threadNum As Int32)
        ' Uses Monitor class to enforce synchronization.
        SyncLock Me
            ' Synchronized: Despite the next conditional, each thread
            ' waits on its predecessor.
            If threadNum Mod 2 = 0 Then
                Thread.Sleep(2000)
            End If
            Console.WriteLine("Start Synched Resource access (Thread={0})", threadNum)
            Thread.Sleep(200)
            Console.WriteLine("Stop Synched Resource access  (Thread={0})", threadNum)
        End SyncLock
    End Sub
End Class

' Without the lock, the method is called in the order in which threads reach it.
Class UnSyncResource
    Public Sub Access(threadNum As Int32)
        ' Does not use Monitor class to enforce synchronization.
        ' The next call throws the thread order.
        If threadNum Mod 2 = 0 Then
            Thread.Sleep(2000)
        End If
        Console.WriteLine("Start UnSynched Resource access (Thread={0})", threadNum)
        Thread.Sleep(200)
        Console.WriteLine("Stop UnSynched Resource access  (Thread={0})", threadNum)
    End Sub
End Class

Public Class App
    Private Shared numAsyncOps As Int32 = 5
    Private Shared asyncOpsAreDone As New AutoResetEvent(false)
    Private Shared SyncRes As New SyncResource()
    Private Shared UnSyncRes As New UnSyncResource()

    Public Shared Sub Main()
        For threadNum As Int32 = 0 To 4
            ThreadPool.QueueUserWorkItem(New WaitCallback(AddressOf SyncUpdateResource), threadNum)
        Next threadNum

        ' Wait until this WaitHandle is signaled.
        asyncOpsAreDone.WaitOne()
        Console.WriteLine(vbTab + vbNewLine + "All synchronized operations have completed." + vbTab + vbNewLine)

        ' Reset the thread count for unsynchronized calls.
        numAsyncOps = 5

        For threadNum As Int32 = 0 To 4
            ThreadPool.QueueUserWorkItem(New WaitCallback(AddressOf UnSyncUpdateResource), threadNum)
        Next threadNum

        ' Wait until this WaitHandle is signaled.
        asyncOpsAreDone.WaitOne()
        Console.WriteLine("\t\nAll unsynchronized thread operations have completed.")
    End Sub

    ' The callback method's signature MUST match that of a
    ' System.Threading.TimerCallback delegate (it takes an Object
    ' parameter and returns void).
    Shared Sub SyncUpdateResource(state As Object)
        ' This calls the internal synchronized method, passing
        ' a thread number.
        SyncRes.Access(CType(state, Int32))

        ' Count down the number of methods that the threads have called.
        ' This must be synchronized, however; you cannot know which thread
        ' will access the value **before** another thread's incremented
        ' value has been stored into the variable.
        If Interlocked.Decrement(numAsyncOps) = 0 Then
            ' Announce to Main that in fact all thread calls are done.
            asyncOpsAreDone.Set()
        End If
    End Sub

    ' The callback method's signature MUST match that of a
    ' System.Threading.TimerCallback delegate (it takes an Object
    ' parameter and returns void).
    Shared Sub UnSyncUpdateResource(state As Object)
        ' This calls the unsynchronized method, passing a thread number.
        UnSyncRes.Access(CType(state, Int32))

        ' Count down the number of methods that the threads have called.
        ' This must be synchronized, however; you cannot know which thread
        ' will access the value **before** another thread's incremented
        ' value has been stored into the variable.
        If Interlocked.Decrement(numAsyncOps) = 0 Then
            ' Announce to Main that in fact all thread calls are done.
            asyncOpsAreDone.Set()
        End If
    End Sub
End Class
using System;
using System.Threading;

// Note: The class whose internal public member is the synchronizing
// method is not public; none of the client code takes a lock on the
// Resource object.The member of the nonpublic class takes the lock on
// itself. Written this way, malicious code cannot take a lock on
// a public object.
class SyncResource
{
    public void Access(Int32 threadNum)
    {
        // Uses Monitor class to enforce synchronization.
        lock (this)
        {
            // Synchronized: Despite the next conditional, each thread
            // waits on its predecessor.
            if (threadNum % 2 == 0)
            {
                Thread.Sleep(2000);
            }
            Console.WriteLine("Start Synched Resource access (Thread={0})", threadNum);
            Thread.Sleep(200);
            Console.WriteLine("Stop Synched Resource access  (Thread={0})", threadNum);
        }
    }
}

// Without the lock, the method is called in the order in which threads reach it.
class UnSyncResource
{
    public void Access(Int32 threadNum)
    {
        // Does not use Monitor class to enforce synchronization.
        // The next call throws the thread order.
        if (threadNum % 2 == 0)
        {
            Thread.Sleep(2000);
        }
        Console.WriteLine("Start UnSynched Resource access (Thread={0})", threadNum);
        Thread.Sleep(200);
        Console.WriteLine("Stop UnSynched Resource access  (Thread={0})", threadNum);
    }
}

public class App
{
    static Int32 numAsyncOps = 5;
    static AutoResetEvent asyncOpsAreDone = new AutoResetEvent(false);
    static SyncResource SyncRes = new SyncResource();
    static UnSyncResource UnSyncRes = new UnSyncResource();

    public static void Main()
    {
        for (Int32 threadNum = 0; threadNum < 5; threadNum++)
        {
            ThreadPool.QueueUserWorkItem(new WaitCallback(SyncUpdateResource), threadNum);
        }

        // Wait until this WaitHandle is signaled.
        asyncOpsAreDone.WaitOne();
        Console.WriteLine("\t\nAll synchronized operations have completed.\t\n");

        // Reset the thread count for unsynchronized calls.
        numAsyncOps = 5;

        for (Int32 threadNum = 0; threadNum < 5; threadNum++)
        {
            ThreadPool.QueueUserWorkItem(new WaitCallback(UnSyncUpdateResource), threadNum);
        }

        // Wait until this WaitHandle is signaled.
        asyncOpsAreDone.WaitOne();
        Console.WriteLine("\t\nAll unsynchronized thread operations have completed.");
    }

    // The callback method's signature MUST match that of a
    // System.Threading.TimerCallback delegate (it takes an Object
    // parameter and returns void).
    static void SyncUpdateResource(Object state)
    {
        // This calls the internal synchronized method, passing
        // a thread number.
        SyncRes.Access((Int32) state);

        // Count down the number of methods that the threads have called.
        // This must be synchronized, however; you cannot know which thread
        // will access the value **before** another thread's incremented
        // value has been stored into the variable.
        if (Interlocked.Decrement(ref numAsyncOps) == 0)
        {
            // Announce to Main that in fact all thread calls are done.
            asyncOpsAreDone.Set();
        }
    }

    // The callback method's signature MUST match that of a
    // System.Threading.TimerCallback delegate (it takes an Object
    // parameter and returns void).
    static void UnSyncUpdateResource(Object state)
    {
        // This calls the unsynchronized method, passing a thread number.
        UnSyncRes.Access((Int32) state);

        // Count down the number of methods that the threads have called.
        // This must be synchronized, however; you cannot know which thread
        // will access the value **before** another thread's incremented
        // value has been stored into the variable.
        if (Interlocked.Decrement(ref numAsyncOps) == 0)
        {
            // Announce to Main that in fact all thread calls are done.
            asyncOpsAreDone.Set();
        }
    }
}
#using <System.dll>

using namespace System;
using namespace System::Threading;

// Note: The class whose internal public member is the synchronizing
// method is not public; none of the client code takes a lock on the
// Resource object.The member of the nonpublic class takes the lock on
// itself. Written this way, malicious code cannot take a lock on
// a public object.
ref class SyncResource
{
public:
    void Access(Int32 threadNum)
    {
        // Uses Monitor class to enforce synchronization.
        Monitor::Enter(this);
        try
        {
            // Synchronized: Despite the next conditional, each thread
            // waits on its predecessor.
            if (threadNum % 2 == 0)
            {
                Thread::Sleep(2000);
            }
            Console::WriteLine("Start Synched Resource access (Thread={0})", threadNum);
            Thread::Sleep(200);
            Console::WriteLine("Stop Synched Resource access  (Thread={0})", threadNum);
        }
        finally
        {
            Monitor::Exit(this);
        }
    }
};

// Without the lock, the method is called in the order in which threads reach it.
ref class UnSyncResource
{
public:
    void Access(Int32 threadNum)
    {
        // Does not use Monitor class to enforce synchronization.
        // The next call throws the thread order.
        if (threadNum % 2 == 0)
        {
            Thread::Sleep(2000);
        }
        Console::WriteLine("Start UnSynched Resource access (Thread={0})", threadNum);
        Thread::Sleep(200);
        Console::WriteLine("Stop UnSynched Resource access  (Thread={0})", threadNum);
    }
};

public ref class App
{
private:
    static Int32 numAsyncOps = 5;
    static AutoResetEvent^ asyncOpsAreDone = gcnew AutoResetEvent(false);
    static SyncResource^ SyncRes = gcnew SyncResource();
    static UnSyncResource^ UnSyncRes = gcnew UnSyncResource();

public:
    static void Main()
    {
        for (Int32 threadNum = 0; threadNum < 5; threadNum++)
        {
            ThreadPool::QueueUserWorkItem(gcnew WaitCallback(SyncUpdateResource), threadNum);
        }

        // Wait until this WaitHandle is signaled.
        asyncOpsAreDone->WaitOne();
        Console::WriteLine("\t\nAll synchronized operations have completed.\t\n");

        // Reset the thread count for unsynchronized calls.
        numAsyncOps = 5;

        for (Int32 threadNum = 0; threadNum < 5; threadNum++)
        {
            ThreadPool::QueueUserWorkItem(gcnew WaitCallback(UnSyncUpdateResource), threadNum);
        }

        // Wait until this WaitHandle is signaled.
        asyncOpsAreDone->WaitOne();
        Console::WriteLine("\t\nAll unsynchronized thread operations have completed.");
    }

    // The callback method's signature MUST match that of a
    // System.Threading.TimerCallback delegate (it takes an Object
    // parameter and returns void).
    static void SyncUpdateResource(Object^ state)
    {
        // This calls the internal synchronized method, passing
        // a thread number.
        SyncRes->Access((Int32) state);

        // Count down the number of methods that the threads have called.
        // This must be synchronized, however; you cannot know which thread
        // will access the value **before** another thread's incremented
        // value has been stored into the variable.
        if (Interlocked::Decrement(numAsyncOps) == 0)
        {
            // Announce to Main that in fact all thread calls are done.
            asyncOpsAreDone->Set();
        }
    }

    // The callback method's signature MUST match that of a
    // System.Threading.TimerCallback delegate (it takes an Object
    // parameter and returns void).
    static void UnSyncUpdateResource(Object^ state)
    {
        // This calls the unsynchronized method, passing a thread number.
        UnSyncRes->Access((Int32) state);

        // Count down the number of methods that the threads have called.
        // This must be synchronized, however; you cannot know which thread
        // will access the value **before** another thread's incremented
        // value has been stored into the variable.
        if (Interlocked::Decrement(numAsyncOps) == 0)
        {
            // Announce to Main that in fact all thread calls are done.
            asyncOpsAreDone->Set();
        }
    }
};

int main()
{
    App::Main();
}

Consulte também

Referência

Monitor

Outros recursos

Threading objetos e recursos