스레드로부터 안전한 구성 요소

스레드 간 리소스 공유는 다중 스레드 프로그래밍에서 자주 사용됩니다. 예를 들어, 여러 스레드에서 공유 데이터베이스에 액세스하거나 시스템 변수 집합을 업데이트할 수도 있습니다. 둘 이상의 스레드에서 동시에 공유 리소스에 경쟁적으로 액세스하면 경합 상태가 발생할 수 있습니다. 한 스레드에서 리소스를 잘못된 상태로 수정하고 나서 다른 스레드에서 이 리소스에 액세스하여 잘못된 상태의 리소스를 사용할 때 경합 상태가 발생합니다. 다음 예제를 참조하십시오.

Public Class WidgetManipulator
Public TotalWidgets as Integer = 0
Public Sub AddWidget()
   TotalWidgets += 1
   Console.WriteLine("Total widgets = " & TotalWidgets.ToString)
End Sub
Public Sub RemoveWidgets()
   TotalWidgets -= 10
End Sub
End Class
public class WidgetManipulator
{
   public int TotalWidgets = 0;
   public void AddWidget()
   {
      TotalWidgets++;
      Console.WriteLine("Total widgets = " + TotalWidgets.ToString());
   }
   public void RemoveWidgets()
   {
      TotalWidgets -= 10;
   }
}

이 클래스에서는 두 개의 메서드를 노출합니다. 첫 번째 메서드인 AddWidget 메서드는 TotalWidgets 필드 값에 1을 더하고 결과를 콘솔에 표시합니다. 두 번째 메서드는 TotalWidgets 값에서 10을 뺍니다. 두 스레드에서 동시에 같은 WidgetManipulator 클래스 인스턴스에 액세스하려는 경우를 가정해 봅니다. 한 스레드에서 AddWidget을 호출하고 동시에 다른 스레드에서 RemoveWidgets을 호출할 수도 있습니다. 이 경우 첫 번째 스레드에서 정확한 값을 보고하기 전에 두 번째 스레드에서 TotalWidgets 값을 변경할 수도 있습니다. 이러한 경합 상태로 인해 올바르지 않은 결과가 보고되고 데이터가 손상될 수 있습니다.

잠금을 사용하여 경합 상태 방지

잠금을 사용하면 코드의 주요 섹션을 경합 상태에서 보호할 수 있습니다. 잠금은 Visual Basic 키워드 SyncLock 문 또는 C# 키워드 lock 문으로 표시되며, 이를 사용하면 실행의 단일 스레드에서 개체에 대한 단독 실행 권한을 얻을 수 있습니다. 다음 예제에서는 잠금을 설정하는 방법을 보여 줍니다.

SyncLock MyObject
' Insert code that affects MyObject.
End SyncLock
lock(MyObject)
{
   // Insert code that affects MyObject.
}

잠금 시작 부분에 이르면 스레드에서 지정된 개체(위 예제에서는 MyObject)에 대한 단독 액세스 권한을 얻을 때까지 개체에 대한 실행이 차단됩니다. 잠금의 끝부분에 도달하면 잠금이 해제되고 실행이 정상적으로 진행됩니다. 참조를 반환하는 개체에만 잠금을 설정할 수 있으며 값을 반환하는 개체에 대해서는 이 방식으로 잠금을 설정할 수 없습니다.

잠금의 단점

잠금을 사용하면 여러 스레드에서 동시에 한 개체에 액세스하지 못하도록 할 수 있지만 이로 인해 성능이 크게 저하될 수도 있습니다. 서로 다른 많은 스레드를 실행하는 프로그램을 가정해 봅니다. 각 스레드에서 특정 개체를 사용해야 하고 그 개체에 대한 단독 잠금을 얻을 때까지 기다린 다음에야 실행할 수 있다면, 스레드들이 모두 실행을 멈춘 상태로 줄지어 대기하게 되어 성능이 떨어질 수 있습니다. 따라서 한 단위로 실행되어야 하는 코드에서만 잠금을 사용해야 합니다. 예를 들어, 상호 의존적인 여러 리소스를 업데이트할 수 있습니다. 이러한 코드를 원자라고 합니다. 원자적으로 실행되는 코드에서만 잠금을 사용하도록 제한하면 좋은 성능을 유지하면서도 데이터의 안전성이 보장되는 다중 스레드 구성 요소를 작성할 수 있습니다.

또한 교착 상태가 발생할 수 있는 상황을 방지해야 합니다. 이 경우 여러 스레드가 서로 공유된 리소스가 해제될 때까지 기다립니다. 예를 들어 스레드 1이 리소스 A를 단독으로 잠그고 있고 리소스 B를 기다리는 반면, 스레드 2는 리소스 B를 잠그고 있고 리소스 A를 기다릴 수 있습니다. 이 경우 두 스레드 모두 진행되지 않습니다. 교착 상태 상황을 방지하려면 세심한 프로그래밍이 필요합니다.

참고 항목

작업

방법: 다중 스레드 실행 조정

방법: 스레드에서 컨트롤 조작

연습: Visual Basic으로 간단한 다중 스레드 구성 요소 만들기

연습: Visual C#으로 간단한 다중 스레드 구성 요소 만들기

참조

BackgroundWorker

개념

이벤트 기반 비동기 패턴 개요

기타 리소스

구성 요소에서 다중 스레딩