SyncLock 陳述式

在執行區塊之前,請先取得陳述式區塊的獨佔鎖定。

語法

SyncLock lockobject  
    [ block ]  
End SyncLock  

組件

lockobject
必要。 評估為物件參考的運算式。

block
選擇性。 取得鎖定時,要執行的陳述式區塊。

End SyncLock
終止 SyncLock 區塊。

備註

SyncLock 陳述式可確保不會出現多個執行緒同時執行該陳述式區塊的情況。 SyncLock 不會讓任一執行緒進入區塊,直到沒有任何其他執行緒在執行該區塊為止。

SyncLock 最常見的用法,是保護資料免於同時由一個以上的執行緒進行更新。 如果運用資料的陳述式必須不中斷地完成,請將這些陳述式置於 SyncLock 區塊內。

受獨佔鎖定所保護的陳述式區塊,有時稱為「關鍵區段」

規則

  • 分支。 您無法從區塊之外,分支到 SyncLock 區塊。

  • 鎖定物件值。 lockobject 的值不得為 Nothing。 您必須先建立鎖定物件,然後才能在 SyncLock 陳述式中使用鎖定物件。

    執行 SyncLock 區塊時,您無法變更 lockobject 的值。 該機制要求鎖定物件要保持不變。

  • 您在 SyncLock 區塊中無法使用 Await 運算子。

行為

  • 機制。 當執行緒到達 SyncLock 陳述式時,會評估 lockobject 運算式並暫停執行,直到它取得由運算式所傳回對該物件的獨佔鎖定為止。 當另一個執行緒到達 SyncLock 陳述式時,要等到第一個執行緒執行 End SyncLock 陳述式之後,才會取得鎖定。

  • 受保護的資料。 如果 lockobjectShared 變數,則獨佔鎖定可避免該類別任一執行個體中的執行緒,在任何其他執行緒還在執行這區塊時,即執行 SyncLock 區塊。 如此可以保護所有執行個體之間共用的資料。

    如果 lockobject 是執行個體變數 (而非 Shared),該鎖定會避免目前執行個體中所執行的執行緒,與相同執行個體中的另一個執行緒,同時執行 SyncLock 區塊。 如此可以保護由個別執行個體所維護的資料。

  • 取得和釋出。 SyncLock 區塊的行為類似於 Try...Finally 建構函式,Try 區塊會取得 lockobject 的獨佔鎖定,而 Finally 區塊會進行釋出。 因此,不論您如何結束 SyncLock 區塊,該區塊都保證一定會釋出鎖定。 即使發生未處理的例外狀況,也是如此。

  • 架構呼叫。 SyncLock 區塊會藉由在 System.Threading 命名空間中呼叫 Monitor 類別的方法 EnterExit,來取得以及釋出獨佔鎖定。

程式設計實務

運算式 lockobject 應該一律評估為專屬於您類別的物件。 您應該宣告 Private 物件變數,來保護屬於目前執行個體的資料,或是宣告 Private Shared 物件變數,來保護所有執行個體通用的資料。

您不應該使用 Me 關鍵字,為執行個體資料提供鎖定物件。 如果您類別外部的程式碼,會參考您類別的執行個體,則程式碼可以為與您的區塊完全不同之 SyncLock 區塊,使用該參考作為鎖定物件,來保護不同的資料。 如此一來,您的類別和另一個類別就可以封鎖彼此,使對方無法執行與其不相關的 SyncLock 區塊。 同樣地,鎖定字串可能會造成問題,因為使用相同字串的處理序中,任一其他程式碼,都共用相同的鎖定。

您也不應該使用 Me.GetType 方法,來為共用資料提供鎖定物件。 這是因為 GetType 一律會針對指定的類別名稱,傳回相同的 Type 物件。 外部程式碼可以對您的類別,呼叫 GetType,並取得您所使用的相同鎖定物件。 如此會導致兩個類別將彼此封鎖在他們的 SyncLock 區塊之外。

範例

描述

下列範例展示維護一份簡單訊息清單的類別。 它會保存陣列中的訊息,以及變數中該陣列最後使用的一個元素。 此 addAnotherMessage 程序會遞增最後一個元素,並儲存新的訊息。 這兩個作業受到 SyncLockEnd SyncLock 陳述式的保護,因為一旦遞增最後一個元素之後,必須先儲存新的訊息,其他任何執行緒才能再次遞增最後一個元素。

如果 simpleMessageList 類別在其所有執行個體之間共用一份訊息清單,則會將 messagesListmessagesLast 變數宣告為 Shared。 在此情況下,變數 messagesLock 也應該是 Shared,如此一來,每個執行個體都會使用單一的鎖定物件。

代碼

Class simpleMessageList
    Public messagesList() As String = New String(50) {}
    Public messagesLast As Integer = -1
    Private messagesLock As New Object
    Public Sub addAnotherMessage(ByVal newMessage As String)
        SyncLock messagesLock
            messagesLast += 1
            If messagesLast < messagesList.Length Then
                messagesList(messagesLast) = newMessage
            End If
        End SyncLock
    End Sub
End Class

描述

下列範例使用執行緒和 SyncLock。 只要 SyncLock 陳述式存在,該陳述式區塊就是關鍵區段,且 balance 絶對不會變成負數。 您可以將 SyncLockEnd SyncLock 陳述式變成註解,以查看排除 SyncLock 關鍵字的效果。

程式碼

Imports System.Threading

Module Module1

    Class Account
        Dim thisLock As New Object
        Dim balance As Integer

        Dim r As New Random()

        Public Sub New(ByVal initial As Integer)
            balance = initial
        End Sub

        Public Function Withdraw(ByVal amount As Integer) As Integer
            ' This condition will never be true unless the SyncLock statement
            ' is commented out:
            If balance < 0 Then
                Throw New Exception("Negative Balance")
            End If

            ' Comment out the SyncLock and End SyncLock lines to see
            ' the effect of leaving out the SyncLock keyword.
            SyncLock thisLock
                If balance >= amount Then
                    Console.WriteLine("Balance before Withdrawal :  " & balance)
                    Console.WriteLine("Amount to Withdraw        : -" & amount)
                    balance = balance - amount
                    Console.WriteLine("Balance after Withdrawal  :  " & balance)
                    Return amount
                Else
                    ' Transaction rejected.
                    Return 0
                End If
            End SyncLock
        End Function

        Public Sub DoTransactions()
            For i As Integer = 0 To 99
                Withdraw(r.Next(1, 100))
            Next
        End Sub
    End Class

    Sub Main()
        Dim threads(10) As Thread
        Dim acc As New Account(1000)

        For i As Integer = 0 To 9
            Dim t As New Thread(New ThreadStart(AddressOf acc.DoTransactions))
            threads(i) = t
        Next

        For i As Integer = 0 To 9
            threads(i).Start()
        Next
    End Sub

End Module

註解

另請參閱