다음을 통해 공유


경합 상태 및 교착 상태

Visual Basic .NET 또는 Visual Basic은 처음으로 Visual Basic 애플리케이션에서 스레드를 사용하는 기능을 제공합니다. 스레드는 경합 상태 및 교착 상태와 같은 디버깅 문제를 소개합니다. 이 문서에서는 이러한 두 가지 문제를 살펴봅니다.

원래 제품 버전: Visual Basic, Visual Basic .NET
원래 KB 번호: 317723

경합 상태가 발생하는 경우

경합 상태는 두 스레드가 공유 변수에 동시에 액세스할 때 발생합니다. 첫 번째 스레드는 변수를 읽고 두 번째 스레드는 변수에서 동일한 값을 읽습니다. 그런 다음 첫 번째 스레드와 두 번째 스레드는 값에 대한 작업을 수행하고 공유 변수에 마지막으로 값을 쓸 수 있는 스레드를 확인하기 위해 경합합니다. 스레드가 이전 스레드가 쓴 값에 대해 쓰기 때문에 해당 값을 마지막으로 쓰는 스레드의 값은 유지됩니다.

경합 상태에 대한 세부 정보 및 예제

각 스레드는 프로세서에서 실행하기 위해 미리 정의된 기간을 할당합니다. 스레드에 할당된 시간이 만료되면 스레드의 컨텍스트는 프로세서를 다음으로 켤 때까지 저장되고 프로세서는 다음 스레드의 실행을 시작합니다.

한 줄 명령으로 경합 상태를 발생시킬 수 있는 방법

다음 예제를 검사하여 경합 상태가 발생하는 방식을 확인합니다. 두 스레드가 있으며 둘 다 합계라는 공유 변수를 업데이트하고 있습니다(어셈블리 코드에서와 같이 dword ptr ds:[031B49DCh] 표시됨).

  • 스레드 1

    Total = Total + val1
    
  • 스레드 2

    Total = Total - val2
    

이전 Visual Basic 코드 컴파일의 어셈블리 코드(줄 번호 포함):

  • 스레드 1

    1. mov eax,dword ptr ds:[031B49DCh]
    2. add eax,edi
    3. jno 00000033
    4. xor ecx,ecx
    5. call 7611097F
    6. mov dword ptr ds:[031B49DCh],eax
    
  • 스레드 2

    1. mov eax,dword ptr ds:[031B49DCh]
    2. sub eax,edi
    3. jno 00000033
    4. xor ecx,ecx
    5. call 76110BE7
    6. mov dword ptr ds:[031B49DCh],eax
    

어셈블리 코드를 살펴보면 프로세서가 간단한 추가 계산을 실행하기 위해 낮은 수준에서 수행하는 작업의 수를 확인할 수 있습니다. 스레드는 프로세서에서 해당 시간 동안 어셈블리 코드의 전부 또는 일부를 실행할 수 있습니다. 이제 이 코드에서 경합 상태가 발생하는 방식을 살펴봅니다.

Total 가 100이고, val1 50이고 val2 , 15입니다. 스레드 1 은 실행할 기회를 얻지만 1~3단계만 완료합니다. 즉 , 스레드 1 은 변수를 읽고 추가를 완료했습니다. 스레드 1 은 이제 새 값 150을 쓰기 위해 기다리고 있습니다. 스레드 1이 중지되면 스레드 2가 완전히 실행됩니다. 즉, 계산된 값(85)을 변수 Total에 기록했습니다. 마지막으로 스레드 1 은 제어권을 되찾고 실행을 완료합니다. 해당 값(150)을 기록합니다. 따라서 스레드 1 이 완료되면 값 Total 이 85가 아닌 150이 됩니다.

이것이 어떻게 주요 문제가 될 수 있는지 확인할 수 있습니다. 이것이 은행 프로그램인 경우 고객은 자신의 계좌에 존재하지 않아야 하는 돈을 갖게 됩니다.

이 오류는 스레드 1프로세서의 시간이 만료되기 전에 실행을 완료한 다음 스레드 2가 실행을 시작할 수 있기 때문에 임의입니다. 이러한 이벤트가 발생하면 문제가 발생하지 않습니다. 스레드 실행은 비결정적이므로 실행 시간 또는 순서를 제어할 수 없습니다. 또한 스레드는 런타임과 디버그 모드에서 다르게 실행할 수 있습니다. 또한 계열에서 각 스레드를 실행하는 경우 오류가 발생하지 않음을 알 수 있습니다. 이러한 임의성 때문에 이러한 오류를 추적하고 디버그하기가 훨씬 더 어려워집니다.

경합 상태가 발생하지 않도록 한 번에 하나의 스레드만 공유 변수에 액세스할 수 있도록 공유 변수를 잠글 수 있습니다. 스레드 1과 스레드 2에도 변수가 필요한 경우 스레드 2가 스레드 1변수를 해제할 때까지 기다리는 동안 스레드 2의 실행이 중지되므로 이 작업을 아예 수행합니다. (자세한 내용은 이 문서의 참조 섹션을 참조 SyncLock 하세요.)

경합 상태의 증상

경합 상태의 가장 일반적인 증상은 여러 스레드 간에 공유되는 변수의 예측할 수 없는 값입니다. 이는 스레드가 실행되는 순서의 예측 불가능성으로 인해 발생합니다. 한 스레드가 승리하고 다른 스레드가 승리하는 경우도 있습니다. 다른 경우에는 실행이 올바르게 작동합니다. 또한 각 스레드가 별도로 실행되는 경우 변수 값이 올바르게 동작합니다.

교착 상태가 발생하는 경우

교착 상태는 두 스레드가 각각 다른 변수를 동시에 잠근 다음 다른 스레드가 이미 잠근 변수를 잠그려고 할 때 발생합니다. 결과적으로 각 스레드는 실행을 중지하고 다른 스레드가 변수를 해제할 때까지 기다립니다. 각 스레드는 다른 스레드가 원하는 변수를 보유하고 있으므로 아무 것도 발생하지 않으며 스레드는 교착 상태로 유지됩니다.

교착 상태에 대한 세부 정보 및 예제

다음 코드에는 두 개의 개체가 LeftVal 있습니다.RightVal

  • 스레드 1

    SyncLock LeftVal
        SyncLock RightVal
            'Perform operations on LeftVal and RightVal that require read and write.
        End SyncLock
    End SyncLock
    
  • 스레드 2

    SyncLock RightVal
        SyncLock LeftVal
            'Perform operations on RightVal and LeftVal that require read and write.
        End SyncLock
    End SyncLock
    

스레드 1이 잠글 LeftVal수 있는 경우 교착 상태가 발생합니다. 프로세서는 스레드 1의 실행을 중지하고 스레드 2실행을 시작합니다. 스레드 2 가 잠기 RightVal 고 잠금을 시도합니다 LeftVal. 잠겨 있으므로 LeftVal 스레드 2가 중지되고 해제될 때까지 기다립니다LeftVal. 스레드 2가 중지되었으므로 스레드 1은 계속 실행할 수 있습니다. 스레드 1은 잠금 RightVal 을 시도하지만 스레드 2가 잠겼기 때문에 잠글 수 없습니다. 결과적으로 스레드 1 은 RightVal을 사용할 수 있게 될 때까지 대기하기 시작합니다. 각 스레드는 다른 스레드가 대기 중인 변수를 잠갔고 두 스레드 모두 해당 스레드가 보유하고 있는 변수의 잠금을 해제하지 않으므로 다른 스레드를 기다립니다.

교착 상태가 항상 발생하는 것은 아닙니다. 스레드 1이 프로세서가 중지하기 전에 두 잠금을 모두 실행하는 경우 스레드 1은 해당 작업을 수행한 다음 공유 변수의 잠금을 해제할 수 있습니다. 스레드 1이 변수의 잠금을 해제한 후 스레드 2는 예상대로 실행을 진행할 수 있습니다.

이러한 코드 조각이 나란히 배치되는 경우 이 오류는 분명해 보이지만 실제로 코드는 코드의 별도 모듈 또는 영역에 나타날 수 있습니다. 이 오류는 동일한 코드에서 올바른 실행과 잘못된 실행이 모두 발생할 수 있으므로 추적하기 어려운 오류입니다.

교착 상태에 대한 증상

교착 상태의 일반적인 증상은 프로그램 또는 스레드 그룹이 응답을 중지한다는 것입니다. 이를 중단이라고도합니다. 두 개 이상의 스레드가 다른 스레드가 잠근 변수를 기다리고 있습니다. 두 스레드 모두 다른 변수를 얻을 때까지 해당 변수를 해제하지 않으므로 스레드는 진행되지 않습니다. 프로그램이 해당 스레드 중 하나 또는 둘 다에서 실행을 완료하기를 기다리는 경우 전체 프로그램이 중단됩니다.

스레드란?

프로세스는 단일 컴퓨터에서 지정된 시간에 실행되는 다른 애플리케이션을 구분하는 데 사용됩니다. 운영 체제는 프로세스를 실행하지 않지만 스레드는 실행합니다. 스레드는 실행 단위입니다. 운영 체제는 스레드 작업의 실행을 위해 스레드에 프로세서 시간을 할당합니다. 단일 프로세스에는 여러 스레드의 실행이 포함될 수 있습니다. 각 스레드는 자체 예외 처리기, 예약 우선 순위 및 스레드가 프로세서에 할당된 시간 동안 스레드의 실행을 완료할 수 없는 경우 스레드의 컨텍스트를 저장하는 데 사용하는 구조 집합을 유지 관리합니다. 컨텍스트는 다음에 스레드가 프로세서 시간을 받을 때까지 유지됩니다. 컨텍스트에는 스레드가 원활하게 실행을 계속하는 데 필요한 모든 정보가 포함됩니다. 이 정보에는 스레드의 프로세서 레지스터 집합과 호스트 프로세스의 주소 공간 내의 호출 스택이 포함됩니다.

참조

자세한 내용은 Visual Studio 도움말에서 다음 키워드를 검색합니다.

  • SyncLock. 개체를 잠글 수 있습니다. 다른 스레드가 동일한 개체를 잠그려고 하면 첫 번째 스레드가 해제될 때까지 차단됩니다. SyncLock의 오용으로 인해 문제가 발생할 수 있으므로 신중하게 사용합니다 SyncLock . 예를 들어 이 명령은 경합 상태를 방지하지만 교착 상태를 일으킬 수 있습니다.

  • InterLocked. 기본 숫자 변수에 대해 스레드로부터 안전한 작업 집합을 선택할 수 있습니다.

자세한 내용은 스레드 및 스레딩을 참조 하세요.