共用方式為


監視器

Monitor 物件會公開同步處理程式碼區域之存取的能力,其方式是使用 Monitor.EnterMonitor.TryEnterMonitor.Exit 方法來鎖定及解除鎖定特定的物件。 一旦您已鎖定程式碼區域,就可使用 Monitor.WaitMonitor.PulseMonitor.PulseAll 方法。 如果 Wait 被保留並等候通知,則會解除鎖定。 當通知 Wait 時,它會傳回並且再次鎖定。 PulsePulseAll 都會發出訊號,通知等候佇列中的下一執行緒繼續執行。

Visual Basic SyncLock 和 C# lock 陳述式 (Statement) 使用 Monitor.Enter 來取得鎖定,以及使用 Monitor.Exit 來釋放它。 使用語言陳述式的好處在於,lock 或 SyncLock 區塊中的所有項目都會包含在 Try 陳述式中。 Try 陳述式具有 Finally 區塊,可以確保鎖定已釋放。

Monitor 會鎖定物件 (也就是參考型別),但不會鎖定實值型別 (Value Type)。 雖然您可以將實值型別傳遞給 EnterExit,但每項呼叫會對它個別進行 Boxed 處理。 由於每項呼叫會建立個別的物件,以致 Enter 永遠不會封鎖,並且不會真的同步處理應該要保護的程式碼。 此外,傳遞給 Exit 的物件與傳遞給 Enter 的物件不同,因此 Monitor 擲回 SynchronizationLockException 時,會發出訊息「從程式碼的未同步化區塊呼叫物件同步化方法」。下列範例說明這些問題。

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

雖然您可以如下列範例所示,在呼叫 EnterExit 之前先對實值型別變數進行 Box 處理,並且傳遞同一個 Boxed 物件到這兩個方法,但是這樣做卻沒有好處。 變數的變更並沒有反映在包裹的複本上,並且無法變更包裹複本的值。

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

務必注意 MonitorWaitHandle 物件之間的差異。 Monitor 物件是純粹 Managed、完全可移植,並且就作業系統資源需求而言更具高效率。 WaitHandle 物件表示作業系統可等候的物件,對 Managed 與 Unmanaged 程式碼之間的同步處理非常有用,並且公開某些進階的作業系統功能,如一次等候許多物件的功能。

下列程式碼範例示範組合使用 Monitor 類別 (使用 lock 和 SyncLock 編譯器陳述式實作)、Interlocked 類別和 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();
}

請參閱

參考

Monitor

其他資源

執行緒處理物件和功能