次の方法で共有


SyncLock ステートメント

ステートメント ブロックを実行する前に、そのブロックに対する排他ロックを取得します。

SyncLock lockobject
    [ block ]
End SyncLock

指定項目

  • lockobject
    必ず指定します。 オブジェクト参照に評価される式です。

  • block
    省略可能です。 ロックを取得したとき実行されるステートメントのブロックです。

  • End SyncLock
    SyncLock ブロックを終了します。

解説

SyncLock ステートメントを使用すると、複数のスレッドがステートメント ブロックを同時に実行するのを回避できます。 SyncLock は、ブロックを実行しているスレッドがなくなるまで、各スレッドがそのブロックに入るのを防ぎます。

ほとんどの場合、SyncLock は、複数のスレッドによってデータが同時に更新されないよう保護するために使用されます。 データを操作するステートメントを、途中で割り込まれることなく最後まで実行する必要がある場合は、ステートメントを SyncLock ブロックの内部に記述してください。

排他ロックに保護されるステートメント ブロックは、クリティカル セクションと呼ばれることもあります。

規則

  • 分岐。 SyncLock ブロックの外部からブロックに分岐することはできません。

  • ロック オブジェクトの値。 lockobject の値を Nothing にすることはできません。 ロック オブジェクトは、SyncLock ステートメントで使用する前に作成する必要があります。

    SyncLock ブロックの実行中に、lockobject の値を変更しないでください。 排他ロックでは、ロック オブジェクトは変更されないでいることが必要です。

動作

  • しくみ。 スレッドは SyncLock ステートメントに到達すると、lockobject 式を評価して、式によって返されたオブジェクトの排他ロックを取得するまで実行を中断します。 別のスレッドが SyncLock ステートメントに到達しても、そのスレッドは最初のスレッドが End SyncLock ステートメントを実行するまでロックを取得しません。

  • データの保護。 lockobject が Shared であれば、排他ロックは他のスレッドが SyncLock ブロックを実行している間は、クラスのどのインスタンス内のスレッドにも SyncLock ブロックを実行させません。 これにより、すべてのインスタンスで共有されているデータを保護できます。

    lockobject が (Shared 変数ではなく) インスタンス変数であれば、排他ロックは現在のインスタンスで動作しているスレッドが、同じインスタンス内の別のスレッドと同時に SyncLock ブロックを実行するのを防ぎます。 これにより、個々のインスタンスで保持されるデータを保護できます。

  • 取得と解放。 SyncLock ブロックは Try...Finally 構造に動作が似ており、Try ブロックで lockobject に対する排他ロックを取得し、Finally ブロックでロックを解放するように動作します。 このため、SyncLock ブロックは、ブロックがどのように終了された場合でも、ロックを必ず解放します。 これは、未処理の例外の場合にも該当します。

  • Framework の呼び出し。 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 つのメッセージ リストをすべてのインスタンスで共有する場合は、messagesList 変数と messagesLast 変数を 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 は負の数にはなりません。 SyncLock ステートメントと End 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

参照

参照

System.Threading

Monitor

スレッドの同期 (C# および Visual Basic)

スレッド処理 (C# および Visual Basic)