티스토리 뷰

728x90

"VB 쓰레드 사용하기"에 이어서 여러개의 쓰레드가 동시에 작업을 수행하는 멀티쓰레딩(Multithreading) 환경에서 꼭 필요한 쓰레드간 동기화에 대해서 주요 기술을 다루고자 합니다. 앞서 다룬 쓰레드 기술에도 Join()과 같이 기본적인 쓰레드 동기화 기능이 있지만 여러 쓰레드가 하나의 자원을 두고 동시에 쓰기 또는 읽기 작업을 하는 경우의 교통 정리에는 한계가 있습니다. 일정 구간의 코드 블럭의 경우에는 다른 쓰레드의 개입없이 코드가 수행되어야 원래의 코드 의도를 달성할 수 있는 등 고급의 쓰레드 동기화 기술이 필요합니다. 쓰레드간 자원 공유의 장점이 유감없이 발휘되려면 이러한 적절한 동기화 기술이 반영되어야만 신뢰성 있고 성능이 보장되면서도 완성도가 높은 프로그램을 제작할 수 있습니다. 아래의 기능들을 사용하려면 Imports System.Threading 참조로 프로그램을 작성합니다.


■ 뮤텍스(Mutex)

특정한 자원이나 코드 블럭에 대한 상호 배제(MUTually EXclusive)를 구현하기 위해 사용하는 시스템 자원으로 "Private mut As New Mutex()"와 같이 클래스 속성으로 선언하고 각 쓰레드의 코드를 뮤텍스 핸들에 대한 권한 얻기(mut.WaitOne())와 작업 수행, 권한 이양(mut.ReleaseMutex())의 단계로 코드를 작성하면 됩니다. 클래스의 Finalize 메소드에서 mut.Dispose()까지 해주면 뮤텍스에 대한 깔끔한 처리가 가능합니다. 뮤텍스에 대한 권한을 얻는 WaitOne() 메소드는 타임아웃 시간을 지정해서 호출할 수도 있는데 지정한 시간내에 권한을 얻지 못하면 False를 리턴합니다. 권한을 얻은 다음에 주의할 것은 권한을 ReleaseMutex()로 적절하게 이양하지 않으면 다른 쓰레드가 무한 대기 하는 교착 상태(Deadlock)에 빠질 수 있다는 점입니다.


■ ManualResetEvent

ManualResetEvent는 이름은 이벤트이지만 뮤텍스처럼 시스템 핸들로 쓰레드의 동작을 제어할 수 있습니다. 차이점이라면 뮤텍스가 여러 쓰레드중에 단 하나의 쓰레드에만 권한을 부여한다면 ManualResetEvent는 모든 쓰레드의 동작에 동일한 영향을 미칩니다. ManualResetEvent 객체에 대하여 WaitOne()을 호출하면 ManualResetEvent가 Set 상태일때 True를 리턴하고 Reset 상태이면 Set 신호를 받을때 까지 대기합니다. 타임아웃을 설정하여 WaitOne()을 호출 했다면 타임아웃 시간이 경과했을 때 False를 리턴합니다.

Private Shared mre As New ManualResetEvent(False)

위와 같이 클래스 속성으로 정의하면 각 쓰레드에서 WaitOne()을 호출하여 ManualResetEvent 핸들을 사용할 수 있습니다.  ManualResetEvent 인스턴스 생성시 False를 인수로 지정한 것은 ManualResetEvent 핸들의 초기상태를 Reset 상태로 하라는 의미로 True로 지정하면 Set()상태에서 ManualResetEvent 핸들을 사용할 수 있게 합니다. 특정 자원에 대하여 일괄적인 금지/해제를 적용할 때 적절합니다. ManualResetEvent 핸들에 대해서는 mre.Set(), mre.Reset()으로 상태를 조정할 수 있습니다.


■ AutoResetEvent

AutoResetEvent 핸들은 ManualResetEvent와 유사하지만 한가지 차이점은 ManualResetEvent가 해당 핸들을 사용하는 모든 쓰레드에 대해서 동일하게 영향을 미친다면 AutoResetEvent 핸들은 특정 쓰레드에 대해서 WaitOne()을 True로 리턴하면(핸들이 Set 상태) 그 즉시 핸들을 Reset 상태로 전환 시켜 핸들이 Set()될 때 까지 다른 쓰레드를 대기하도록 합니다.


■ SyncLock

데이터베이스의 커밋 블록처럼 쓰레드 코드의 일정한 블럭이 수행중이면 다른 쓰레드에서는 해당 코드 블럭을 수행할 수 없도록 보장해 주는 장치입니다. 

SyncLock 오브젝트

......

End SyncLock

사용법은 간단해서 SyncLock 구문에 컨크롤 하고 싶은 오브젝트를 기술하고 End SyncLock 사이에 연관 코드를 기술하면 됩니다. 컨트롤 대상 오브젝트는 클래스, 모듈, 배열, 인터페이스에 대한 오브젝트가 모두 가능합니다. 이러한 코드 블럭의 대표적인 예제로 계좌 입금/출금 처리를 들수 있는데 현재 잔액을 읽어서 지정한 금액 만큼을 증액 또는 감액하여 다시 저장하는 과정에 다른 쓰레드가 끼어 든다면 예상치 못한 결과를 산출할 수 있기 때문에 이런한 코드를 SyncLock으로 묶으면 안전한 처리를 수행할 수 있습니다.


■ Interlocked

Interlocked 클래스는 특정한 변수에 대하여 여러 쓰레드가 동시에 업데이트하는 것을 제어해 주는 클래스입니다. "a = a + 1" 대신에 System.Threading.Interlocked.Increment(a) 로 기술하면 시스템에서 알아서 교통정리하여 해당 변수에 대한 +1 동작이 정확하게 수행하도록 해줍니다. Interlocked 클래스를 통해서 수행할 수 있는 작업으로는 다음과 같은 것들이 있습니다.

  • Increment(정수 변수) : 정수 변수 값을 +1하여 해당 변수에 다시 저장
  • Decrement(정수 변수) : 정수 변수 값을 -1하여 해당 변수에 다시 저장
  • Add(정수 변수, 값) : 정수 변수 값에 추가값을 반영하여 해당 변수에 다시 저장
  • Exchange(변수, 값) : 지정 변수에 값을 저장하고 변수에 있던 원래 값을 리턴


■ ReaderWriterLock

특정 리소스를 여러 쓰레드가 공유하지만 대부분의 접근은 읽기만 하고 아주 일부만 쓰기를 하는 경우에 적당합니다. 여러개의 읽기 쓰레드로 동작하고 쓰기 쓰레드는 단 한개만 동작하게 됩니다. 뮤텍스를 사용하듯이 "Private rwl As New System.Threading.ReaderWriterLock()"로 선언하고 읽기는 rwl.AcquireReaderLock()과 rwl.ReleaseReaderLock()로 권한 얻기와 이양을 수행합니다. 쓰기는 rwl.AcquireWriterLock()과 rwl.ReleaseWriterLock() 쌍으로 권한 얻기와 이양을 수행하면 됩니다. 각 쓰레드는 하나의 ReaderWriterLock에 대하여 읽기와 쓰기 잠금을 동시에 가질 수 없는데 읽기 잠금 상태에서 권한한 이양없이 UpgradeToWriterLock()으로 전환하거나 DowngradeFromWriterLock()으로 복귀할 수 있습니다.


728x90
댓글
최근에 올라온 글
최근에 달린 댓글
«   2024/04   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
글 보관함