Condividi tramite


Monitor

Gli oggetti Monitor espongono la possibilità di sincronizzare l'accesso a un'area di codice accettando e rilasciando un blocco su un determinato oggetto tramite i metodi Monitor.Enter, Monitor.TryEnter e Monitor.Exit. Una volta creato un blocco su un'area di codice, è possibile utilizzare i metodi Monitor.Wait Monitor.Pulse e Monitor.PulseAll. Waitrilascia l'eventuale blocco e attende la notifica. Quando Wait riceve la notifica, restituisce un risultato e ottiene nuovamente il blocco. Sia Pulse chePulseAll segnalano al thread successivo nella coda di attesa di procedere.

Le istruzioni SyncLock di Visual Basic e lock di C# utilizzano Monitor.Enter per acquisire il blocco e Monitor.Exit per rilasciarlo. Il vantaggio dell'utilizzo delle istruzioni di linguaggio è che tutto il contenuto di un blocco lock o SyncLock è incluso in un'istruzione Try. L'istruzione Try include un blocco Finally per garantire il rilascio del blocco.

Monitor consente di bloccare gli oggetti, ovvero i tipi di riferimento, ma non i tipi di valore. Sebbene sia possibile passare un tipo di valore a Enter e Exit, il boxing viene eseguito separatamente per ogni singola chiamata. Poiché con ogni chiamata viene creato un oggetto separato, Enter non si blocca mai e il codice apparentemente protetto non è in realtà sincronizzato. Inoltre, poiché l'oggetto passato a Exit è diverso dall'oggetto passato a Enter, Monitor genera un evento SynchronizationLockException con il messaggio "Metodo di sincronizzazione dell'oggetto chiamato da un blocco di codice non sincronizzato". Nell'esempio che segue vengono illustrati questi problemi.

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);
}

Sebbene sia possibile eseguire il boxing di una variabile di tipo di valore prima di chiamare Enter e Exit, come illustrato nell'esempio seguente, e passare lo stesso oggetto sottoposto a boxing a entrambi i metodi, questa operazione non comporta alcun vantaggio. Le modifiche apportate alla variabile non vengono riflesse nella copia boxed di cui non è possibile modificare il valore.

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 notare la distinzione tra l'utilizzo degli oggetti Monitor e WaitHandle. Gli oggetti Monitor sono completamente gestiti, completamente portabili e potrebbero essere più efficienti in termini di requisiti di risorse del sistema operativo. Gli oggetti WaitHandle rappresentano oggetti waitable del sistema operativo, sono utili per la sincronizzazione tra codice gestito e non gestito ed espongono alcune funzionalità avanzate del sistema operativo, come la capacità di attendere su molti oggetti contemporaneamente.

Nell'esempio di codice seguente viene illustrato l'utilizzo combinato della classe Monitor, implementata con le istruzioni del compilatore lock e SyncLock, della classe Interlocked e della classe AutoResetEvent.

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();
}

Vedere anche

Riferimenti

Monitor

Altre risorse

Oggetti e funzionalità del threading