次の方法で共有


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 ブロックがlockobjectの排他ロックを取得し、Finally ブロックによって解放されるTry...Finally構造と同様に動作します。 このため、 SyncLock ブロックは、ブロックを終了する方法に関係なく、ロックの解放を保証します。 これは、ハンドルされない例外の場合でも当てはまります。

  • フレームワーク呼び出し。 SyncLock ブロックは、System.Threading名前空間のMonitor クラスのEnterメソッドとExit メソッドを呼び出すことによって、排他ロックを取得および解放します。

プログラミングプラクティス

lockobject式は、常にクラスに排他的に属するオブジェクトに評価される必要があります。 現在のインスタンスに属するデータを保護する Private オブジェクト変数、またはすべてのインスタンスに共通するデータを保護する Private Shared オブジェクト変数を宣言する必要があります。

インスタンス データのロック オブジェクトを指定するには、 Me キーワードを使用しないでください。 クラスの外部のコードがクラスのインスタンスへの参照を持っている場合は、その参照を SyncLock ブロックのロック オブジェクトとして使用して、異なるデータを保護できます。 このようにして、クラスと他のクラスが相互に関連のない SyncLock ブロックを実行するのをブロックできます。 同様に、同じ文字列を使用するプロセス内の他のコードが同じロックを共有するため、文字列のロックは問題になる可能性があります。

また、 Me.GetType メソッドを使用して共有データのロック オブジェクトを指定しないでください。 これは、 GetType は、特定のクラス名に対して常に同じ Type オブジェクトを返すからです。 外部コードは、クラスで GetType を呼び出し、使用しているのと同じロック オブジェクトを取得できます。 これにより、2 つのクラスが SyncLock ブロックから互いをブロックすることになります。

例示

説明

次の例は、メッセージの単純なリストを保持するクラスを示しています。 配列内のメッセージと、その配列の最後に使用された要素を変数に保持します。 addAnotherMessage プロシージャは、最後の要素をインクリメントし、新しいメッセージを格納します。 これらの 2 つの操作は、 SyncLock ステートメントと End SyncLock ステートメントによって保護されます。最後の要素がインクリメントされると、他のスレッドが最後の要素を再度インクリメントする前に、新しいメッセージを格納する必要があるためです。

simpleMessageList クラスがすべてのインスタンス間でメッセージの 1 つのリストを共有している場合、変数messagesListmessagesLastSharedとして宣言されます。 この場合、変数 messagesLockSharedし、すべてのインスタンスで 1 つのロック オブジェクトが使用されるようにする必要があります。

Code

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負の数になることはありません。 SyncLockステートメントとEnd SyncLock ステートメントをコメント アウトして、SyncLock キーワードを除外した場合の影響を確認できます。

Code

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

コメント

こちらも参照ください