Single Thread Apartment(STA) 는 Visual BASIC 에서 ActiveX DLL을 개발하거나 AX EXE 를 개발하면 default 로 갖게 되는Thread Model 입니다. 또한, Multiple Thread Apartment(MTA)로 구성 될 수 없습니다. 오히려 MTA 는 이해하기가 쉬운데, STA가 좀처럼 이해 하기가 쉽지 않은 모델이라 몇 가지 정리해볼까 합니다.
Thread
Thread 는 실행 최소 단위입니다. Application 관점에서 main () 함수에서 나열된 구문을 모두 해석하여 시스템에서 수행이 되면, 그냥 하나의 Thread가 수행된 겁니다. 만일, 나열된 구문 중에Thread를Fork 하는CreateThread API가 호출된다면, 기존의Thread는 그냥 흘러가고 동시에 또 다른Thread 가 만들어져서 걔도 걔 나름대로 흘러가게 됩니다.
Apartment
Thread가 실행되다가 필요에 의해서 개발된AX(또는COM)에서 제공되는 Object를 호출하고 수행할 필요가 있을 겁니다. 이 때 이와 같은Object 는 임의의Apartment 에 위치하고 그 위치를Thread가 접근하게 됩니다. 이Apartment 는 일종의 보호 장벽일 수 있고, 뭔가 끼리끼리 모이기 위한 공간이라고 생각할 수도 있을 겁니다. 하여튼, CoInitialize()/CoInitializeEx() 함수는Apartment 를 만들고, CoUninitialize() 는 해당Apartment 를 해제합니다. 그 안에서 COM 이 존재합니다. VB COM 은 Single Thread Apartment 라는 곳에 위치합니다. 해당Apartment 에서VB COM 을 접근하는Thread는 단지1개만 존재해야 합니다. 그래서Single Thread Apartment 라는 용어가 됩니다.
Thread affinity
Single Thread Apartment 안에서 1개 이상의Thread가 동일한 또는 다른Object 를 이용(Object Context의 생성이 되겠죠.)하려고 한다면 STA 안에서는Message Queue 를 이용하게 합니다. Thread의 요청이Queue 안에 순차적으로 쌓이고 Queue out 하면서 한번에 하나씩 처리를 하면서 동기화 합니다. 물론, MTA는 이와 같은 구조가 아니므로, 동기화에 해당하는 강구(WaitForSingleObject/WaitForMultipleObjejcts API 이용)가 필요합니다만, STA는 내부적으로 이런 동기화 지원을 합니다. 그러므로, Thread affinity 라는 용어도 사용합니다. 일반적으로 동기화가 되기 때문에Thread Local Storage 와 같은 영역이 보장이 됩니다.
Causality
조금은 다른 말이 될 수 있는 데, DCOM 구조가 있습니다. 필요에 의해서 다른Process에서 수행되고 있는Object 를 이용하는 경우도 있습니다. DLLHOST.exe에서 surrogate 되고 있는COM의 경우가 대표적인 상황입니다. 이 경우에는 다른Process안의STA 에Object가 존재하고 있지만, RPC Channel 을 통해서 해당Object를 이용할 수 있습니다. 만일, A 라는Process안의 임의의 Thread가 A process안의STA 에 있는Object a 를 호출하고, 해당Object a 는 RPC Channel을 통해 B 라는Process안의 임의의 Thread로 하여금 MTA 혹은STA 안의 b 라는Object를 이용하는 구조를 생각해 봅시다. 이런 경우A Process의Thread와B process의 Thread는 당연히 서로 다른 구별된Thread입니다. 하지만, Logical 하게는 하나의Thread 로 , 하나의 수행흐름으로 보입니다. 하여 이와 같은 논리적인Thread를 Causality라고 합니다. 그래서 이들은 하나의 CID 값을 내부적으로 갖습니다. 보통 MSDTC의Transaction 처리는 하나의CID로 길게 수행이 됩니다.
*Reentrancy
Causality 예에서는 다른 Process의Apartment에 접근하는Cross apartment형태입니다. Cross apartment 의 경우RPC Channel을 통해서Call 한 후Callback 을Asynchronous 하게Return 받아야 하는 구조입니다. MTA Model 에서는 Apartment에 다른 여러 개의 Thread가 존재하기 때문에 임의의Thread가Callback을return 받지만, STA Model 은 그것이 불가능합니다. 하여, 이러한 Cross Apartment Call을 위해서 마찬가지로Message Loop 를 이용합니다. message Queue에 던져놓고Callback 을 기다리는 겁니다. 그러므로, Cross Apartment Call에 대한 Callback은 일단Blocking이 되고 해당Apartment는 다른Thread의 요청을 받을 수 있습니다. 이 경우에Reentrancy 가 발생합니다. 또 다른Thread 또는Causality가 해당Apartment에 진입할 수 있습니다. 기존의Cross Apartment Call에 대한Causality 는 종료되지 않은 상태로 또 다른Causality 가 진행됩니다. 이 경우 두 번째Causality가STA를 소유하고 있으므로, Cross Apartment Callback은 두 번째Causality 가 완료될 때 까지는 처리되지 못합니다. 물론, 두 번째Causality가 또 다시Cross Apartment Call이 될 수 도 있고 또 다른 세 번째, 네 번째Causality 가 들어오게 되어Dead Lock 과 같은 현상이 발생할 수도 있습니다. 물론, 두 번째Causality가 종료되고 첫 번째Callback이 이어서 종료되는 것이 정상적인 동작이 될 수도 있습니다.
이와 같은STA Reentrancy 현상은TLS 에도 영향을 미칩니다. 특히, VB Application의 경우는TLS에Global Variable이나, App, Err 내부적인Objects를 저장하고 있습니다. 이와 같은Reentrancy 는TLS가share되는 현상이 발생할 수 있기 때문에TLS Lock( *Activity Lock의 형태*)이나, Multiple Thread 환경에서 사용되는VB Application은Global Variable 을 사용하지 말라는Guide (http://support.microsoft.com/?id=815053) 를 하고 있습니다.
*Activity Lock 은 COM+ 에서 제공하는 Serialization을 이용한다고 보시면 됩니다.
*COM+ Catalog에서 STA Pool에 존재하는Thread 수를Monitoring 하다 보면, Reentrancy 로 인하여Max Pool 수보다 많은 수가 나오는 것도 경험할 수 있습니다.
STA는 Thread의 동기화를 걱정 할 필요가 없기때문에 편리하긴 하지만, Message Queue 의구조나 Message Queue를 사용하면서 발생하는 Thread switch, 그리고, Reentrancy에 의한 Dead Lock 이 발생할 수 도 있음을 고려해야 합니다. 그러므로, Multiple Threads 환경인 COM+ 나 IIS, 그리고, IE에서 VB AX(STA 기반의Component)를 사용 하는것은 많은 Issue를 유발 할 수 밖에 없을 것 같습니다. ( 사실VB 로만든 Component가 아니라면 더이상 STA는 보기 어려울 듯 합니다만.. 이제 그수명이 한 10개월정도 남은 것 같습니다.. )
덧글