OLE 스레딩 모델의 설명 및 작업

  • 아티클
  • 2023. 07. 17.
  •  

이 문서의 내용

  1. 요약
  2. 개요
  3. 자세한 내용
  4. 단일 스레드 아파트 모델

이 문서에서는 OLE 스레딩 모델에 대해 설명합니다.

원래 제품 버전: OLE 스레딩 모델
원래 KB 번호: 150777

요약

COM 개체는 프로세스의 여러 스레드에서 사용할 수 있습니다. "STA(단일 스레드 아파트)" 및 "다중 스레드 아파트"(MTA)라는 용어는 개체와 스레드 간의 관계, 개체 간의 동시성 관계, 메서드 호출이 개체에 전달되는 방법 및 스레드 간에 인터페이스 포인터를 전달하는 규칙을 설명하는 개념 프레임워크를 만드는 데 사용됩니다. 구성 요소와 해당 클라이언트는 현재 COM에서 지원되는 다음 두 아파트 모델 중에서 선택합니다.

  1. STA(단일 스레드 아파트 모델): 프로세스에서 하나 이상의 스레드가 COM을 사용하고 COM 개체에 대한 호출이 COM에 의해 동기화됩니다. 인터페이스는 스레드 간에 마샬링됩니다. 지정된 프로세스의 스레드 하나만 COM을 사용하는 단일 스레드 아파트 모델의 퇴화 사례를 단일 스레딩 모델이라고 합니다. 이전에는 STA 모델을 단순히 "아파트 모델"이라고도 했습니다.
  2. MTA(다중 스레드 아파트 모델): 하나 이상의 스레드가 COM을 사용하고 MTA와 연결된 COM 개체에 대한 호출은 호출자와 개체 간의 시스템 코드의 인터치 없이 MTA와 연결된 모든 스레드에서 직접 이루어집니다. 여러 동시 클라이언트가 다중 프로세서 시스템에서 동시에 개체를 더 많거나 적게 호출할 수 있으므로 개체는 자체적으로 내부 상태를 동기화해야 합니다. 인터페이스는 스레드 간에 마샬링되지 않습니다. 이전에는 이 모델을 "자유 스레드 모델"이라고도 했습니다.
  3. STA 모델과 MTA 모델 모두 동일한 프로세스에서 사용할 수 있습니다. 이를 "혼합 모델" 프로세스라고도 합니다.

MTA는 NT 4.0에서 도입되었으며 DCOM95를 사용하는 Windows 95에서 사용할 수 있습니다. STA 모델은 Windows NT 3.51 및 Windows 95뿐만 아니라 DCOM95를 사용하는 NT 4.0 및 Windows 95에도 존재합니다.

개요

COM의 스레딩 모델은 서로 다른 스레딩 아키텍처를 사용하여 함께 작동하는 구성 요소에 대한 메커니즘을 제공합니다. 또한 필요한 구성 요소에 동기화 서비스를 제공합니다. 예를 들어 특정 개체는 단일 스레드에서만 호출되도록 설계될 수 있으며 클라이언트의 동시 호출을 동기화하지 않을 수 있습니다. 이러한 개체가 여러 스레드에서 동시에 호출되면 충돌하거나 오류가 발생합니다. COM은 스레딩 아키텍처의 상호 운용성을 처리하기 위한 메커니즘을 제공합니다.

스레드 인식 구성 요소에도 동기화 서비스가 필요한 경우가 많습니다. 예를 들어 OLE/ActiveX 컨트롤, 현재 위치 활성 포함 및 ActiveX 문서와 같은 GUI(그래픽 사용자 인터페이스)가 있는 구성 요소에는 COM 호출 및 창 메시지의 동기화 및 직렬화가 필요합니다. COM은 복잡한 동기화 코드 없이 이러한 구성 요소를 작성할 수 있도록 이러한 동기화 서비스를 제공합니다.

"아파트"에는 여러 가지 관련 측면이 있습니다. 첫째, 스레드가 COM 개체 집합과 어떻게 관련되는지와 같이 동시성을 고려하기 위한 논리적 구문입니다. 둘째, 프로그래머가 COM 환경에서 기대하는 동시성 동작을 수신하기 위해 준수해야 하는 규칙 집합입니다. 마지막으로, 프로그래머가 COM 개체와 관련하여 스레드 동시성을 관리하는 데 도움이 되는 시스템 제공 코드입니다.

"아파트"라는 용어는 프로세스가 "아파트"라는 관련 있지만 다른 "로캘"의 집합으로 세분화되는 "건물"과 같은 불연속적인 엔터티로 생각되는 은유에서 비롯됩니다. 아파트는 개체와 경우에 따라 스레드 간에 연결을 만드는 "논리적 컨테이너"입니다. 스레드는 아파트가 아니지만 STA 모델의 아파트와 논리적으로 연결된 단일 스레드가 있을 수 있습니다. 모든 개체가 하나의 아파트와만 연결되어 있지만 개체는 아파트가 아닙니다. 그러나 아파트는 단지 논리적 인 구조 이상입니다; 해당 규칙은 COM 시스템의 동작을 설명합니다. 아파트 모델의 규칙을 따르지 않으면 COM 개체가 제대로 작동하지 않습니다.

자세한 내용

STA(단일 스레드 아파트)는 특정 스레드와 연결된 COM 개체 집합입니다. 이러한 개체는 스레드에 의해 만들어지거나, 더 정확하게는 스레드에서 COM 시스템에 먼저 노출(일반적으로 마샬링)하여 아파트와 연결됩니다. STA는 개체 또는 프록시가 "있는" 장소로 간주됩니다. 개체 또는 프록시가 다른 아파트(동일하거나 다른 프로세스)에서 액세스해야 하는 경우 해당 인터페이스 포인터를 새 프록시가 만들어진 해당 아파트로 마샬링해야 합니다. 아파트 모델의 규칙을 따르는 경우 해당 개체에 대해 동일한 프로세스의 다른 스레드에서 직접 호출할 수 없습니다. 지정된 아파트 내의 모든 개체가 단일 스레드에서 실행된다는 규칙을 위반하는 입니다. 이 규칙은 STA에서 실행되는 대부분의 코드가 추가 스레드에서 실행되는 경우 제대로 작동하지 못하기 때문에 존재합니다.

STA와 연결된 스레드는 들어오는 호출을 수신하기 위해 연결된 개체에 대한 창 메시지를 호출 CoInitialize 하거나 CoInitializeEx(NULL, COINIT_APARTMENTTHREADED) 검색하고 디스패치해야 합니다. COM은 이 문서의 뒷부분에 설명된 대로 창 메시지를 사용하여 STA의 개체에 대한 호출을 디스패치하고 동기화합니다.

"main STA"는 지정된 프로세스 내에서 호출하거나 CoInitializeEx(NULL, COINIT_APARTMENTTHREADED) 먼저 호출 CoInitialize 하는 스레드입니다. 프로세스의 기본 STA는 이 문서의 뒷부분에 설명된 대로 일부 프로시전 개체가 항상 기본 STA에 로드되므로 모든 COM 작업이 완료될 때까지 활성 상태로 유지되어야 합니다.

Windows NT 4.0 및 DCOM95는 다중 스레드 아파트 (MTA)라는 아파트의 새로운 유형을 소개합니다. MTA는 프로세스의 스레드 집합과 연결된 COM 개체 집합이므로 모든 스레드가 시스템 코드의 처리 없이 개체 구현을 직접 호출할 수 있습니다. MTA의 모든 개체에 대한 인터페이스 포인터는 마샬링할 필요 없이 MTA와 연결된 스레드 간에 전달될 수 있습니다. 호출 CoInitializeEx(NULL, COINIT_MULTITHREADED) 하는 프로세스의 모든 스레드는 MTA와 연결됩니다. 위에서 설명한 STA와 달리 MTA의 스레드는 들어오는 호출을 수신하기 위해 연결된 개체에 대한 창 메시지를 검색하고 디스패치할 필요가 없습니다. COM은 MTA의 개체에 대한 호출을 동기화하지 않습니다. MTA의 개체는 여러 동시 스레드의 상호 작용으로 인해 내부 상태가 손상되는 것을 방지해야 하며 서로 다른 메서드 호출 간에 남아 있는 Thread-Local Storage의 콘텐츠에 대해 어떠한 가정도 할 수 없습니다.

프로세스에는 많은 수의 STA가 있을 수 있지만 최대 하나의 MTA가 있을 수 있습니다. MTA는 하나 이상의 스레드로 구성됩니다. STA에는 각각 하나의 스레드가 있습니다. 스레드는 적어도 하나의 아파트에 속합니다. 개체는 하나의 아파트에만 속합니다. 인터페이스 포인터는 항상 아파트 간에 마샬링되어야 합니다(마샬링 결과는 프록시가 아닌 직접 포인터일 수 있음). 에 대한 CoCreateFreeThreadedMarshaler아래 정보를 참조하세요.

프로세스는 COM에서 제공하는 스레딩 모델 중 하나를 선택합니다. STA 모델 프로세스에는 하나 이상의 STA가 있으며 MTA가 없습니다. MTA 모델 프로세스에는 하나 이상의 스레드가 있는 하나의 MTA가 있으며 STA가 없습니다. 혼합 모델 프로세스에는 하나의 MTA와 임의의 수의 STA가 있습니다.

단일 스레드 아파트 모델

COM은 창 메시지를 사용하여 이 모델의 개체에 대한 호출 배달을 동기화하고 디스패치하기 때문에 STA의 스레드는 창 메시지를 호출 CoInitialize 하거나 CoInitializeEx(NULL, COINIT_APARTMENTTHREADED) 검색하고 디스패치해야 합니다. 자세한 내용은 아래 REFERENCES 섹션을 참조하세요.

STA 모델을 지원하는 서버:

STA 모델에서 개체에 대한 호출은 창에 게시된 창 메시지가 동기화되는 것과 동일한 방식으로 COM에 의해 동기화됩니다. 호출은 개체를 만든 스레드에 창 메시지를 사용하여 전달됩니다. 따라서 개체의 스레드가 호출하고 DispatchMessage 호출 Get/PeekMessage 을 수신해야 합니다. COM은 각 STA와 연결된 숨겨진 창을 만듭니다. 이 숨겨진 창에 게시된 창 메시지를 사용하여 COM 런타임에서 STA 외부에서 개체에 대한 호출이 개체의 스레드로 전송됩니다. 개체의 STA와 연결된 스레드가 메시지를 검색하고 디스패치하면 COM에서 구현한 숨겨진 창의 창 프로시저도 메시지를 받습니다. COM 런타임은 COM 소유 스레드에서 STA 스레드로의 호출 양쪽에 있기 때문에 COM 런타임에서 STA와 연결된 스레드를 "후크"하는 데 사용됩니다. COM 런타임(현재 STA 스레드에서 실행 중)은 COM 제공 스텁을 통해 개체의 해당 인터페이스 메서드로 "up"을 호출합니다. 메서드 호출에서 반환되는 실행 경로는 "up" 호출을 반대로 합니다. 호출은 스텁 및 COM 런타임으로 돌아갑니다. 그러면 컨트롤이 창 메시지를 통해 COM 런타임 스레드로 다시 전달되고 COM 채널을 통해 원래 호출자에게 반환됩니다.

여러 클라이언트가 STA 개체를 호출하면 STA에서 사용되는 제어 메커니즘을 전송하여 호출이 메시지 큐에 자동으로 큐에 대기됩니다. 개체는 STA가 메시지를 검색하고 디스패치할 때마다 호출을 받습니다. 이러한 방식으로 COM에서 호출을 동기화하고 호출이 개체의 STA와 연결된 단일 스레드에서 항상 전달되기 때문에 개체의 인터페이스 구현에서 동기화를 제공할 필요가 없습니다.

참고

인터페이스 메서드 구현이 메서드 호출을 처리하는 동안 메시지를 검색하고 디스패치하면 개체를 다시 입력할 수 있으며, 이로 인해 동일한 STA에서 다른 호출이 개체에 전달됩니다. 일반적인 방법은 STA 개체가 COM을 사용하여 나가는(아파트 간/크로스 프로세스) 호출을 수행하는 경우입니다. 이는 메시지를 처리하는 동안 메시지를 검색하고 디스패치하는 경우 창 프로시저를 다시 입력할 수 있는 방식과 동일합니다. COM은 동일한 스레드에서 다시 입력하는 것을 방지하지 않지만 동시 실행을 방지합니다. 또한 COM 관련 재진입을 관리할 수 있는 방법을 제공합니다. 자세한 내용은 아래 REFERENCES 섹션을 참조하세요. 메서드 구현이 해당 아파트에서 호출하지 않거나 메시지를 검색하고 디스패치하지 않으면 개체가 다시 입력되지 않습니다.

STA 모델의 클라이언트 책임:

STA 모델을 사용하는 프로세스 및/또는 스레드에서 실행되는 클라이언트 코드는 다음을 사용하여 CoMarshalInterThreadInterfaceInStreamCoGetInterfaceAndReleaseStream아파트 간에 개체의 인터페이스를 마샬링해야 합니다. 예를 들어 클라이언트의 Apartment 1에 인터페이스 포인터가 있고 Apartment 2를 사용해야 하는 경우 Apartment 1은 인터페이스를 마샬 CoMarshalInterThreadInterfaceInStream링해야 합니다. 이 함수에서 반환된 스트림 개체는 스레드로부터 안전하며 인터페이스 포인터는 Apartment 2에서 액세스할 수 있는 직접 메모리 변수에 저장되어야 합니다. Apartment 2는 기본 개체에서 인터페이스의 CoGetInterfaceAndReleaseStream 경계를 해제하고 개체에 액세스할 수 있는 프록시에 대한 포인터를 다시 가져오기 위해 이 스트림 인터페이스를 전달해야 합니다.

일부 in-proc 개체가 main-apartment에 로드되기 때문에 클라이언트가 모든 COM 작업을 완료할 때까지 지정된 프로세스의 주 아파트는 활성 상태로 유지되어야 합니다. (자세한 내용은 아래에 자세히 설명되어 있습니다).

다중 스레드 아파트 모델

MTA는 호출 CoInitializeEx(NULL, COINIT_MULTITHREADED)한 프로세스의 모든 스레드에서 만들거나 노출하는 개체의 컬렉션입니다.

참고

COM의 현재 구현에서는 COM을 명시적으로 초기화하지 않는 스레드가 MTA의 일부가 될 수 있습니다. COM을 초기화하지 않는 스레드는 프로세스에서 하나 이상의 다른 스레드가 이전에 호출 CoInitializeEx(NULL, COINIT_MULTITHREADED)한 후에 COM 사용을 시작하는 경우에만 MTA의 일부입니다. (클라이언트 스레드가 명시적으로 수행되지 않은 경우 COM 자체가 MTA를 초기화했을 수도 있습니다(예: STA 호출 CoGetClassObject/CoCreateInstance[Ex] 과 연결된 스레드). "ThreadingModel=Free"로 표시된 CLSID에서 COM은 클래스 개체가 로드되는 MTA를 암시적으로 만듭니다.) 아래의 스레딩 모델 상호 운용성에 대한 정보를 참조하세요.

그러나 이는 특정 상황에서 액세스 위반과 같은 문제를 일으킬 수 있는 구성입니다. 따라서 COM 작업을 수행해야 하는 각 스레드는 COM 작업을 호출 CoInitializeEx 하여 COM을 초기화한 다음 COM 작업을 완료할 때 호출 CoUninitialize하는 것이 좋습니다. MTA를 "불필요하게" 초기화하는 비용은 최소화됩니다.

COM은 이 모델의 창 메시지를 사용하여 개체에 대한 호출을 전달하지 않으므로 MTA 스레드는 메시지를 검색하고 디스패치할 필요가 없습니다.

  • MTA 모델을 지원하는 서버:
  • MTA 모델에서 개체에 대한 호출은 COM에 의해 동기화되지 않습니다. 여러 클라이언트가 서로 다른 스레드에서 이 모델을 지원하는 개체를 동시에 호출할 수 있으며, 개체는 이벤트, 뮤텍스, 세마포 등의 동기화 개체를 사용하여 인터페이스/메서드 구현에서 동기화를 제공해야 합니다. MTA 개체는 개체의 프로세스에 속하는 COM에서 만든 스레드 풀을 통해 여러 Out-of-process 클라이언트에서 동시 호출을 받을 수 있습니다. MTA 개체는 MTA와 연결된 여러 스레드에서 여러 In-Process 클라이언트에서 동시 호출을 받을 수 있습니다.
  • MTA 모델의 클라이언트 책임:
  • MTA 모델을 사용하는 프로세스 및/또는 스레드에서 실행되는 클라이언트 코드는 자체 및 다른 MTA 스레드 간에 개체의 인터페이스 포인터를 마샬링할 필요가 없습니다. 대신 한 MTA 스레드는 다른 MTA 스레드에서 가져온 인터페이스 포인터를 직접 메모리 포인터로 사용할 수 있습니다. 클라이언트 스레드가 out-of-process 개체를 호출하면 호출이 완료될 때까지 일시 중단됩니다. MTA와 연결된 모든 애플리케이션에서 만든 스레드가 나가는 호출에서 차단되는 동안 MTA와 연결된 개체에 호출이 도착할 수 있습니다. 이 경우 일반적으로 수신 호출은 COM 런타임에서 제공하는 스레드에서 전달됩니다. 메시지 필터(IMessageFilter)는 MTA 모델에서 사용할 수 없습니다.

혼합 스레딩 모델

혼합 스레딩 모델을 지원하는 프로세스는 하나의 MTA와 하나 이상의 STA를 사용합니다. 인터페이스 포인터는 모든 아파트 간에 마샬링되어야 하지만 MTA 내에서 마샬링하지 않고도 사용할 수 있습니다. STA의 개체에 대한 호출은 COM에서 동기화되어 하나의 스레드에서만 실행되지만 MTA의 개체에 대한 호출은 실행되지 않습니다. 그러나 STA에서 MTA로의 호출은 일반적으로 시스템 제공 코드를 통과하고 STA 스레드에서 MTA 스레드로 전환한 후 개체로 전달됩니다.

참고

직접 포인터를 사용할 수 있는 경우 및 STA 스레드가 MTA와 처음 연결된 개체로 직접 호출하고 그 반대로 여러 아파트에서 직접 호출할 수 있는 방법에 대한 자세한 내용은 아래의 SDK 설명서 CoCreateFreeThreadedMarshaler() 및 해당 API에 대한 논의를 참조하세요.

스레딩 모델 선택

구성 요소는 혼합 스레딩 모델을 사용하여 STA 모델, MTA 모델 또는 둘의 조합을 지원하도록 선택할 수 있습니다. 예를 들어 광범위한 I/O를 수행하는 개체는 I/O 대기 시간 동안 인터페이스 호출을 허용하여 클라이언트에 대한 최대 응답을 제공하기 위해 MTA를 지원하도록 선택할 수 있습니다. 또는 사용자와 상호 작용하는 개체는 거의 항상 들어오는 COM 호출을 해당 GUI 작업과 동기화하도록 STA를 지원하도록 선택합니다. COM에서 동기화를 제공하므로 STA 모델을 지원하는 것이 더 쉽습니다. 개체가 동기화를 구현해야 하기 때문에 MTA 모델을 지원하는 것이 더 어렵지만 COM에서 제공하는 전체 인터페이스 호출보다는 코드의 작은 섹션에 동기화가 사용되기 때문에 클라이언트에 대한 응답이 더 좋습니다.

STA 모델은 Microsoft Transaction Server(MTS, 이전에는 코드명 "바이퍼")에서도 사용되므로 MTS 환경 내에서 실행하려는 DLL 기반 개체는 STA 모델을 사용해야 합니다. MTA 모델에 대해 구현된 개체는 일반적으로 MTS 환경에서 제대로 작동합니다. 그러나 불필요한 스레드 동기화 기본 형식을 사용하기 때문에 덜 효율적으로 실행됩니다.

In-Proc 서버의 지원되는 스레딩 모델 표시

스레드는 초기화하지 않고 COM을 호출 CoInitializeEx(NULL, COINIT_MULTITHREADED) 하거나 사용하는 경우 MTA 모델을 사용합니다. 스레드는 호출 CoInitialize 하는 CoInitializeEx(NULL, COINIT_APARTMENTTHREADED)경우 STA 모델을 사용합니다.

API는 CoInitialize 클라이언트 코드 및 패키지된 개체에 대한 아파트 제어를 제공합니다. EXE는 COM 런타임의 시작 코드가 원하는 방식으로 COM을 초기화할 수 있기 때문입니다.

그러나 DLL(In-proc) COM 서버는 DLL 서버가 로드될 때까지 해당 API가 호출되기 때문에 호출 CoInitialize/CoInitializeEx 되지 않습니다. 따라서 DLL 서버는 COM이 지원하는 스레딩 모델을 COM에 알리기 위해 레지스트리를 사용하여 시스템이 호환되는 방식으로 작동하도록 해야 합니다. 다음과 같이 이 용도로 호출된 ThreadingModel 구성 요소의 CLSID\InprocServer32 키의 명명된 값이 사용됩니다.

  • ThreadingModel 값이 없음: 단일 스레딩 모델을 지원합니다.
  • ThreadingModel=Apartment: STA 모델을 지원합니다.
  • ThreadingModel=Both: STA 및 MTA 모델을 지원합니다.
  • ThreadingModel=Free: MTA만 지원합니다.

참고

ThreadingModel 는 이전 버전의 Win32 설명서에서 잘못 문서화된 InprocServer32의 하위 키가 아닌 명명된 값입니다.

프로시전 서버의 스레딩 모델은 이 문서의 뒷부분에 설명되어 있습니다. in-proc 서버가 여러 형식의 개체(각각 고유한 CLSID가 있는 개체)를 제공하는 경우 각 형식에 다른 ThreadingModel 값이 있을 수 있습니다. 즉, 스레딩 모델은 코드 패키지/DLL이 아닌 CLSID 단위입니다. 그러나 "부트스트랩"하고 모든 프록시 내 서버(DLLGetClassObject(), DLLCanUnloadNow())를 쿼리하는 데 필요한 API 진입점은 여러 스레드를 지원하는 모든 프로시전 서버(아파트, 둘 다 또는 무료 값 의미 ThreadingModel )에 대해 스레드로부터 안전해야 합니다.

앞에서 설명한 것처럼 Out-of-process 서버는 ThreadingModel 값을 사용하여 자신을 표시하지 않습니다. 대신, 그들은 사용 CoInitialize 하거나 CoInitializeEx. COM의 "서로게이트" 기능(예: 시스템 제공 서로게이트 DLLHOST.EXE)을 사용하여 Out-of-process가 실행되기를 예상하는 DLL 기반 서버는 DLL 기반 서버에 대한 규칙을 따릅니다. 이 경우 특별한 고려 사항이 없습니다.

클라이언트 및 개체가 다른 스레딩 모델을 사용하는 경우

클라이언트와 개체가 서로 다른 프로세스에 있고 COM이 클라이언트에서 개체로 호출을 전달하는 데 관여하기 때문에 다른 스레딩 모델을 사용하는 경우에도 클라이언트와 out-of-process 개체 간의 상호 작용은 바로 진행됩니다. COM은 클라이언트와 서버 간에 상호 연결되므로 스레딩 모델의 상호 운용을 위한 코드를 제공합니다. 예를 들어 여러 STA 또는 MTA 클라이언트에서 STA 개체를 동시에 호출하는 경우 COM은 해당 창 메시지를 서버의 메시지 큐에 배치하여 호출을 동기화합니다. 개체의 STA는 메시지를 검색하고 디스패치할 때마다 한 번의 호출을 받습니다. 스레딩 모델 상호 운용성의 모든 조합은 클라이언트와 Out-of-process 개체 간에 허용되고 완전히 지원됩니다.

클라이언트와 다른 스레딩 모델을 사용하는 프로시전 내 개체 간의 상호 작용은 더 복잡합니다. 서버가 프로시버에 있지만 COM은 클라이언트와 개체 간에 자체적으로 개입해야 합니다. 예를 들어 STA 모델을 지원하도록 설계된 프로시저 내 개체는 클라이언트의 여러 스레드에서 동시에 호출될 수 있습니다. COM은 개체가 이러한 동시 액세스를 위해 설계되지 않았기 때문에 클라이언트 스레드가 개체의 인터페이스에 직접 액세스하도록 허용할 수 없습니다. 대신 COM은 호출이 동기화되고 개체를 "포함하는" STA와 연결된 스레드에서만 수행되도록 해야 합니다. 복잡성이 더해졌음에도 불구하고 스레딩 모델 상호 운용성의 모든 조합은 클라이언트와 프로시저 내 개체 간에 허용됩니다.

OUT-of-process(EXE 기반) 서버의 스레딩 모델

다음은 세 가지 범주의 Out-of-process 서버이며, 각 서버는 해당 클라이언트에서 사용하는 스레딩 모델에 관계없이 COM 클라이언트에서 사용할 수 있습니다.

  1. STA 모델 서버:
  2. 서버는 하나 이상의 STA에서 COM 작업을 수행합니다. 들어오는 호출은 COM에 의해 동기화되고 개체가 만들어진 STA와 연결된 스레드에서 전달됩니다. 클래스 팩터리 메서드 호출은 클래스 팩터리를 등록한 STA와 연결된 스레드에서 전달됩니다. 개체와 클래스 팩터리는 동기화를 구현할 필요가 없습니다. 그러나 구현자는 여러 STA에서 사용하는 전역 변수에 대한 액세스를 동기화해야 합니다. 서버는 STA 간에 다른 서버에서 인터페이스 포인터를 사용하고 CoMarshalInterThreadInterfaceInStreamCoGetInterfaceAndReleaseStream 마샬링해야 합니다. 필요에 따라 서버는 각 STA에서 구현 IMessageFilter 하여 COM에서 호출 배달의 측면을 제어할 수 있습니다. 퇴화 사례는 COM이 하나의 STA에서 작동하는 단일 스레딩 모델 서버입니다.
  3. MTA 모델 서버:
  4. 서버는 MTA에 속하는 하나 이상의 스레드에서 COM 작업을 수행합니다. COM에서 호출을 동기화하지 않습니다. COM은 서버 프로세스에 스레드 풀을 만들고 클라이언트 호출은 이러한 스레드에서 전달됩니다. 스레드는 메시지를 검색하고 디스패치할 필요가 없습니다. 개체 및 클래스 팩터리는 동기화를 구현해야 합니다. 서버는 스레드 간에 인터페이스 포인터를 마샬링할 필요가 없습니다.
  5. 혼합 모델 서버:
  6. 자세한 내용은 이 문서의 "혼합 스레딩 모델" 섹션을 참조하세요.

클라이언트의 스레딩 모델

클라이언트에는 다음 세 가지 범주가 있습니다.

  1. STA 모델 클라이언트:
  2. 클라이언트는 하나 이상의 STA와 연결된 스레드에서 COM 작업을 수행합니다. 클라이언트는 STA 간에 인터페이스 포인터를 사용하고 CoMarshalInterThreadInterfaceInStreamCoGetInterfaceAndReleaseStream 마샬링해야 합니다. 퇴화 사례는 COM이 하나의 STA에서 작동하는 단일 스레딩 모델 클라이언트입니다. 클라이언트의 스레드는 발신 호출을 할 때 COM 제공 메시지 루프를 입력합니다. 클라이언트는 나가는 호출 및 기타 동시성 문제를 기다리는 동안 창 메시지의 콜백 및 처리를 관리하는 데 사용할 IMessageFilter 수 있습니다.
  3. MTA 모델 클라이언트:
  4. 클라이언트는 MTA에 속하는 하나 이상의 스레드에서 COM 작업을 수행합니다. 클라이언트는 스레드 간에 인터페이스 포인터를 마샬링할 필요가 없습니다. 클라이언트에서 .를 사용할 IMessageFilter수 없습니다. 클라이언트의 스레드는 OUT-of-process 개체에 대한 COM 호출을 수행하면 일시 중단되고 호출이 반환되면 다시 시작됩니다. 들어오는 호출은 COM에서 만든 관리되는 스레드에 도착합니다.
  5. 혼합 모델 클라이언트:
  6. 자세한 내용은 이 문서의 "혼합 스레딩 모델" 섹션을 참조하세요.

프로시전 내(DLL 기반) 서버의 스레딩 모델

4가지 범주의 in-proc 서버가 있으며, 각 범주는 해당 클라이언트에서 사용하는 스레딩 모델에 관계없이 COM 클라이언트에서 사용할 수 있습니다. 그러나 프로시저 내 서버는 일반적으로 COM이 클라이언트 아파트 간의 인터페이스를 마샬링해야 하기 때문에 스레딩 모델 상호 운용성을 지원하는 경우 구현하는 모든 사용자 지정(비 시스템 정의) 인터페이스에 대한 마샬링 코드를 제공해야 합니다. 네 가지 범주는 다음과 같습니다.

  1. 단일 스레딩("main" STA)을 지원하는 In-proc Server - 값 없음 ThreadingModel :
  2. 이 서버에서 제공하는 개체는 생성된 동일한 클라이언트 STA에서 액세스해야 합니다. 또한 서버는 동일한 스레드(주 STA와 DllCanUnloadNow연결된 스레드)에서 모든 진입점(예: DllGetClassObject 및 전역 데이터)에 액세스할 것으로 예상합니다. COM에서 다중 스레딩이 도입되기 전에 존재했던 서버는 이 범주에 속합니다. 이러한 서버는 여러 스레드에서 액세스하도록 설계되지 않았으므로 COM은 프로세스의 기본 STA에서 서버에서 제공하는 모든 개체를 만들고 개체에 대한 호출은 주 STA와 연결된 스레드에서 전달됩니다. 다른 클라이언트 아파트는 프록시를 통해 개체에 액세스할 수 있습니다. 다른 아파트의 호출은 프록시에서 기본 STA(스레드 간 마샬링)의 스텁으로 이동한 다음 개체로 이동합니다. 이 마샬링을 사용하면 COM에서 개체에 대한 호출을 동기화할 수 있으며 호출은 개체가 만들어진 STA에서 전달됩니다. 스레드 간 마샬링이 직접 호출에 비해 느리므로 여러 STA(범주 2)를 지원하도록 이러한 서버를 다시 작성하는 것이 좋습니다.
  3. 단일 스레드 아파트 모델(여러 STA)을 지원하는 In-proc 서버 - 다음으로 ThreadingModel=Apartment표시됨서버가 모든 개체를 만들기 위해 클래스 팩터리의 인스턴스를 하나만 제공하는 경우 여러 클라이언트 STA에서 액세스하기 때문에 클래스 팩터리 구현도 다중 스레드 사용을 위해 설계되어야 합니다. 서버가 호출될 때마다 DllGetClassObject 클래스 팩터리의 새 인스턴스를 만드는 경우 클래스 팩터리는 스레드로부터 안전할 필요가 없습니다. 그러나 구현자는 전역 변수에 대한 액세스를 동기화해야 합니다.클래스 팩터리를 통해 STA 개체를 만드는 모든 클라이언트는 개체에 대한 직접 포인터를 가져옵니다. 이는 클라이언트의 주 STA만 개체에 대한 직접 포인터를 가져오고 개체를 만드는 다른 모든 STA가 프록시를 통해 개체에 액세스하는 단일 스레드 in-proc 개체와 다릅니다. 스레드 간 마샬링이 직접 호출에 비해 느리기 때문에 여러 STA를 지원하도록 단일 스레드 in-proc 서버를 변경하여 속도를 향상시킬 수 있습니다.
  4. 클래스 팩터리에서 만든 COM 개체는 스레드로부터 안전할 필요가 없습니다. 그러나 전역 변수의 액세스는 구현자에 의해 동기화되어야 합니다. 스레드에서 만든 개체는 항상 해당 스레드를 통해 액세스되고 개체에 대한 모든 호출은 COM에서 동기화됩니다. 개체를 만든 STA와 다른 클라이언트 아파트는 프록시를 통해 개체에 액세스해야 합니다. 이러한 프록시는 클라이언트가 아파트 간의 인터페이스를 마샬링할 때 만들어집니다.
  5. 이 서버에서 제공하는 개체는 생성된 동일한 클라이언트 STA에서 액세스해야 합니다. 따라서 단일 스레드 in-proc 서버에서 제공하는 개체와 유사합니다. 그러나 이 서버에서 제공하는 개체는 프로세스의 여러 STA에서 만들 수 있으므로 서버는 다중 스레드 사용을 위해 진입점 및 DllCanUnloadNow전역 데이터와 같은 DllGetClassObject 진입점을 디자인해야 합니다. 예를 들어 프로세스의 두 STA가 in-proc 개체의 두 인스턴스를 동시에 DllGetClassObject 만드는 경우 두 STA에서 동시에 호출될 수 있습니다. 마찬가지로 코드 DllCanUnloadNow 가 서버에서 실행되는 동안 서버가 언로드되지 않도록 서버가 보호되도록 작성해야 합니다.
  6. MTA만 지원하는 In-proc 서버 - 다음으로 ThreadingModel=Free표시
  7. 이 서버에서 제공하는 개체는 MTA에만 안전합니다. 자체 동기화를 구현하고 동시에 여러 클라이언트 스레드에서 액세스합니다. 이 서버에는 STA 모델과 호환되지 않는 동작이 있을 수 있습니다. (예를 들어, STA의 메시지 펌프를 중단하는 방식으로 Windows 메시지 큐를 사용합니다.) 또한 개체의 스레딩 모델을 "Free"로 표시하여 개체의 구현자는 다음을 나타냅니다. 이 개체는 모든 클라이언트 스레드에서 호출할 수 있지만 이 개체는 인터페이스 포인터를 만든 스레드에 직접 전달할 수 있으며 이러한 스레드는 이러한 포인터를 통해 호출할 수 있습니다. 따라서 클라이언트가 이 개체에 대한 클라이언트 구현 개체(예: 싱크)에 대한 인터페이스 포인터를 전달하는 경우 만든 스레드에서 이 인터페이스 포인터를 통해 다시 호출하도록 선택할 수 있습니다. 클라이언트가 STA인 경우 싱크 개체를 만든 스레드와 다른 스레드에서 직접 호출하면 오류가 발생합니다(위의 2에 설명된 대로). 따라서 COM은 항상 STA와 연결된 스레드의 클라이언트가 프록시를 통해서만 이러한 종류의 in-proc 개체에 액세스할 수 있도록 합니다. 또한 이러한 개체는 STA 스레드에서 직접 실행할 수 있으므로 자유 스레드 마샬러를 사용하여 집계해서는 안 됩니다.
  8. 아파트 모델 및 자유 스레딩을 지원하는 In-proc 서버 - 다음으로 ThreadingModel=Both표시이러한 개체를 만드는 모든 아파트는 항상 프록시 포인터가 아닌 직접 포인터 ThreadingModel "Both" 를 가져오므로 개체는 STA에 로드될 때 개체에 대한 ThreadingModel "Free" 성능 향상을 제공합니다.

    참고

    자유 스레드 마샬러를 사용하여 집계할 때는 주의해야 합니다. 예를 들어 자유 스레드 마샬러를 사용하여 집계하는 것으로 ThreadingModel "Both" 표시된 개체에 "Apartment"인 다른 개체에 대한 인터페이스 포인터인 데이터 멤버가 ThreadingModel 있다고 가정합니다. 그런 다음 STA가 첫 번째 개체를 만들고 만드는 동안 첫 번째 개체가 두 번째 개체를 만든다고 가정합니다. 위에서 설명한 규칙에 따라 첫 번째 개체는 이제 두 번째 개체에 대한 직접 포인터를 보유합니다. 이제 STA가 첫 번째 개체에 대한 인터페이스 포인터를 다른 아파트로 마샬링한다고 가정합니다. 첫 번째 개체는 자유 스레드 마샬러를 사용하여 집계되므로 첫 번째 개체에 대한 직접 포인터가 두 번째 아파트에 지정됩니다. 두 번째 아파트가 이 포인터를 통해 호출하고 이 호출로 인해 첫 번째 개체가 두 번째 개체에 대한 인터페이스 포인터를 통해 호출되는 경우 두 번째 개체가 두 번째 아파트에서 직접 호출되지 않기 때문에 오류가 발생했습니다. 첫 번째 개체가 직접 포인터가 아닌 두 번째 개체에 대한 프록시에 대한 포인터를 보유하는 경우 다른 오류가 발생합니다. 시스템 프록시는 하나의 아파트와만 연결된 COM 개체이기도 합니다. 그들은 특정 순환을 피하기 위해 자신의 아파트를 추적합니다. 따라서 개체가 실행 중인 스레드와 다른 아파트와 연결된 프록시에서 호출하는 개체는 프록시에서 RPC_E_WRONG_THREAD 반환을 수신하고 호출이 실패합니다.

  9. ThreadingModel "Both" 개체는 MTA 액세스를 위해 설계되었으므로(내부적으로 스레드로부터 안전함) 제공된 CoCreateFreeThreadedMarshaler마샬러를 집계하여 성능을 단축할 수 있습니다. 이 시스템 제공 개체는 모든 호출 개체에 집계되며 사용자 지정 마샬링 프로세스의 모든 아파트로 개체에 대한 직접 포인터를 마샬링합니다. STA 또는 MTA에 관계없이 모든 아파트의 클라이언트는 프록시를 통해서가 아니라 개체에 직접 액세스할 수 있습니다. 예를 들어 STA 모델 클라이언트는 STA1에서 in-proc 개체를 만들고 개체를 STA2로 마샬링합니다. 개체가 자유 스레드 마샬러를 사용하여 집계되지 않으면 STA2는 프록시를 통해 개체에 대한 액세스 권한을 얻습니다. 이 경우 자유 스레드 마샬러는 개체에 대한 직접 포인터를 STA2에 제공합니다.
  10. 이 서버에서 제공하는 개체는 자체 동기화를 구현하고 여러 클라이언트 아파트에서 동시에 액세스합니다. 또한 이 개체는 프록시를 통해서가 아니라 클라이언트 프로세스의 STA 또는 MTA에서 직접 만들어지고 사용됩니다. 이 개체는 STA에서 직접 사용되므로 서버는 스레드 간에 다른 서버에서 개체의 인터페이스를 마샬링해야 스레딩에 적합한 방식으로 개체에 대한 액세스가 보장됩니다. 또한 개체의 스레딩 모델을 "Both"로 표시하여 개체의 구현자는 다음을 명시합니다. 이 개체는 모든 클라이언트 스레드에서 호출할 수 있지만 이 개체에서 클라이언트로의 콜백은 개체가 콜백 개체에 대한 인터페이스 포인터를 받은 아파트에서만 수행됩니다. COM을 사용하면 클라이언트 프로세스의 MTA뿐만 아니라 STA에서 직접 이러한 개체를 만들 수 있습니다.

클라이언트와 In-process 개체 간의 스레딩 모델 상호 운용성

스레딩 모델 상호 운용성의 모든 조합은 클라이언트와 In-process 개체 간에 허용됩니다.

COM을 사용하면 모든 STA 모델 클라이언트가 클라이언트의 기본 STA에서 개체를 만들고 액세스하고 호출 CoCreateInstance[Ex]한 클라이언트 STA에 마샬링하여 단일 스레딩 in-proc 개체와 상호 작동할 수 있습니다.

클라이언트의 MTA가 STA 모델 in-proc 서버를 만드는 경우 COM은 클라이언트에서 "호스트" STA를 스핀업합니다. 이 호스트 STA는 개체를 만들고 인터페이스 포인터는 MTA로 다시 마샬링됩니다. 마찬가지로 STA가 MTA in-proc 서버를 만들 때 COM은 개체가 만들어지고 STA로 다시 마샬링되는 호스트 MTA를 스핀업합니다. 단일 스레딩 모델과 MTA 모델 간의 상호 운용성은 단일 스레딩 모델이 STA 모델의 퇴화 사례일 뿐이므로 유사하게 처리됩니다.

COM이 클라이언트 아파트 간의 인터페이스를 마샬링해야 하는 상호 운용성을 지원하려는 경우 프로시저 내 서버가 구현하는 모든 사용자 지정 인터페이스에 대해 마샬링 코드를 제공해야 합니다. 자세한 내용은 아래 REFERENCES 섹션을 참조하세요.

반환된 클래스 팩터리 개체와 스레딩 모델 간의 관계

아파트에 "로드"되는 in-proc 서버에 대한 정확한 정의는 다음 두 단계에 설명되어 있습니다.

  1. in-proc 서버 클래스를 포함하는 DLL이 운영 체제 로더에 의해 이전에 로드되지 않은 경우(프로세스 주소 공간에 매핑) 해당 작업이 수행되고 COM은 DLL에서 내보낸 함수의 DLLGetClassObject 주소를 가져옵니다. 이전에 모든 아파트와 연결된 스레드에서 DLL을 로드한 경우 이 단계를 건너뜁습니다.
  2. COM은 "loading" 아파트와 연결된 스레드(또는 MTA의 경우 스레드 중 하나)를 사용하여 필요한 클래스의 CLSID를 요청하는 DLL에서 내보낸 함수를 호출 DllGetClassObject 합니다. 반환된 팩터리 개체는 클래스의 개체 인스턴스를 만드는 데 사용됩니다.
  3. 두 번째 단계(COM의 호출 DllGetClassObject )는 클라이언트가 호출 CoGetClassObject/CoCreateIntance[Ex]할 때마다 동일한 아파트 내에서도 발생합니다. 즉, DllGetClassObject 동일한 아파트와 연결된 스레드에서 여러 번 호출될 수 있습니다. 이 모든 것은 해당 아파트의 클라이언트 수에 따라 해당 클래스의 클래스 팩터리 개체에 액세스하려고 하는 클라이언트 수에 따라 달라집니다.

클래스 구현의 작성자이며, 궁극적으로 지정된 클래스 집합의 DLL 패키지 작성자는 함수 호출에 대한 응답으로 반환할 팩터리 개체를 완전히 자유롭게 결정할 수 DllGetClassObject 있습니다. DLL 패키지의 작성자는 단일 DLL 전체 DllGetClassObject() 진입점의 코드 "숨김"이 수행할 작업을 결정할 수 있는 첫 번째이자 잠재적으로 최종적인 권한이 있기 때문에 궁극적인 말을 합니다. 세 가지 일반적인 가능성은 다음과 같습니다.

  1. DllGetClassObject 는 호출 스레드당 고유한 클래스 팩터리 개체를 반환합니다(즉, STA당 하나의 클래스 팩터리 개체와 MTA 내에서 잠재적으로 여러 클래스 팩터리를 의미).
  2. DllGetClassObject 호출 스레드의 ID 또는 호출 스레드와 연결된 아파트 종류에 관계없이 항상 동일한 클래스 팩터리 개체를 반환합니다.
  3. DllGetClassObject 는 호출 아파트당 고유한 클래스 팩터리 개체를 반환합니다(STA 및 MTA의 아파트당 하나).

호출과 반환된 클래스 팩터리 개체(예: 호출 DllGetClassObjectDllGetClassObject당 새 클래스 팩터리 개체)의 관계에 대한 다른 가능성이 있지만 현재는 유용하지 않은 것으로 보입니다.

In-Proc 서버에 대한 클라이언트 및 개체 스레딩 모델 요약

다음 표에서는 클라이언트 스레드가 in-proc 서버로 구현된 클래스를 처음 호출 CoGetClassObject 할 때 서로 다른 스레딩 모델 간의 상호 작용을 요약합니다.

클라이언트/스레드 종류:

  • 클라이언트는 "main" STA(호출 CoInitialize 할 첫 번째 스레드 또는 CoInitializeEx 플래그)와 COINIT_APARTMENTTHREADED 연결된 스레드에서 실행되고 있습니다. 이 STA0(단일 스레딩 모델이라고도 함)를 호출합니다.
  • 클라이언트가 다른 STA [ASCII 150]에 연결된 스레드에서 실행 중이며 이 STA*를 호출합니다.
  • 클라이언트가 MTA에 연결된 스레드에서 실행되고 있습니다.

DLL 서버 종류:

  • 서버에 키가 없습니다 ThreadingModel . 이 "없음"을 호출합니다.
  • 서버가 "아파트"로 표시되어 있습니다. 이 "Apt"를 호출합니다.
  • 서버가 "무료"로 표시됩니다.
  • 서버가 "둘 다"로 표시됩니다.

아래 표를 읽을 때는 서버를 아파트로 "로드"하는 위에서 만든 정의를 염두에 두어야 합니다.

콘솔
Client         Server                 Result
STA0           None                   Direct access; server loaded into STA0  
STA*           None                   Proxy access; server loaded into STA0.  
MTA            None                   Proxy access; server loaded into STA0; STA0 created automatically by COM if necessary;  
STA0           Apt                    Direct access; server loaded into STA0  
STA*           Apt                    Direct access; server loaded into STA*  
MTA            Apt                    Proxy access; server loaded into an STA created automatically by COM.
STA0           Free                   Proxy access; server is loaded into MTA MTA created automatically by COM if necessary.
STA*           Free                   Same as STA0->Free
MTA            Free                   Direct access
STA0           Both                   Direct access; server loaded into STA0
STA*           Both                   Direct access; server loaded into STA*
MTA            Both                   Direct access; server loaded into the MTA

참조

및 IMessageFilter 인터페이스에 CoRegisterMessageFilter() 대한 SDK 설명서입니다.

Posted by gurupia
,