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 클래스의 EnterExit 메서드를 호출하여 배타적 잠금을 획득하고 해제합니다.

프로그래밍 사례

lockobject 식은 항상 클래스에만 속하는 개체로 평가되어야 합니다. 현재 인스턴스에 속한 데이터를 보호하려면 Private 개체 변수를 선언해야 하고, 모든 인스턴스에 공통된 데이터를 보호하려면 Private Shared 개체 변수를 선언해야 합니다.

인스턴스 데이터에 대한 잠금 개체를 제공하기 위해 Me 키워드를 사용해서는 안 됩니다. 클래스 외부의 코드에 클래스 인스턴스에 대한 참조가 있는 경우 해당 참조를 사용자 블록과 완전히 다른 SyncLock 블록에 대한 잠금 개체로 사용하여 다른 데이터를 보호할 수 있습니다. 이러한 방식으로 사용자의 클래스와 다른 클래스는 서로 관련되지 않은 SyncLock 블록을 실행하지 못하도록 차단할 수 있습니다. 마찬가지로 문자열에 대한 잠금은 동일한 문자열을 사용하는 프로세스의 다른 코드가 동일한 잠금을 공유하므로 문제가 될 수 있습니다.

또한 공유 데이터에 대한 잠금 개체를 제공하기 위해 Me.GetType 메서드를 사용해서는 안 됩니다. 이는 GetType이 지정된 클래스 이름에 대해 항상 동일한 Type 개체를 반환하기 때문입니다. 외부 코드는 클래스에서 GetType을 호출하고 사용 중인 것과 동일한 잠금 개체를 가져올 수 있습니다. 이로 인해 두 클래스가 SyncLock 블록에서 서로를 차단하게 됩니다.

예제

설명

다음 예에서는 간단한 메시지 목록을 유지 관리하는 클래스를 보여 줍니다. 배열에 메시지를 보관하고 해당 배열에서 마지막으로 사용된 요소를 변수에 보관합니다. addAnotherMessage 프로시저는 마지막 요소를 증가시키고 새 메시지를 저장합니다. 이 두 작업은 마지막 요소가 증가되면 다른 스레드가 마지막 요소를 다시 증가시키기 전에 새 메시지를 저장해야 하므로 SyncLockEnd SyncLock 문으로 보호됩니다.

simpleMessageList 클래스가 모든 인스턴스 간에 하나의 메시지 목록을 공유하는 경우 변수 messagesListmessagesLastShared로 선언됩니다. 이 경우 모든 인스턴스에서 사용되는 단일 잠금 개체가 있도록 변수 messagesLockShared여야 합니다.

코드

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

주석

참고 항목