In-Process 서버 스레딩 문제
인프로세스 서버는 CoInitialize, CoInitializeEx, 혹은 OleInitialize를 호출하여 스레딩 모델을 설정하지 않습니다. 스레드 인식 DLL 기반 또는 In-process 개체의 경우 레지스트리에서 스레딩 모델을 설정해야 합니다. 스레딩 모델을 지정하지 않는 경우 기본 모델은 프로세스당 단일 스레드입니다. 모델을 지정하려면 ThreadingModel 값을 레지스트리의 InprocServer32 키에 추가합니다.
클래스 개체의 인스턴스화를 지원하는 DLL은 DllGetClassObject및 DllCanUnloadNow함수를 구현하고 내보내야 합니다. 클라이언트가 DLL에서 지원하는 클래스의 인스턴스를 원하는 경우 CoGetClassObject 호출(직접 또는 CoCreateInstance호출을 통해)은 DLL에서 개체가 구현될 때 DllGetClassObject 호출하여 해당 클래스 개체에 대한 포인터를 가져옵니다. 따라서 DllGetClassObject 여러 클래스 개체를 제공하거나 스레드 안전한 단일 개체를 제공할 수 있어야 합니다(기본적으로 내부 참조 수에 대해 InterlockedIncrement/ InterlockedDecrement를 사용).
이름에서 알 수 있듯이 DllCanUnloadNow 호출되어 DLL을 구현하는 DLL이 사용 중인지 여부를 확인하여 호출자가 사용하지 않는 경우 안전하게 언로드할 수 있도록 합니다. 모든 스레드에서CoFreeUnusedLibraries에 대한 호출은 항상 메인 아파트의 스레드를 통해 라우팅되어 DllCanUnloadNow를 호출합니다.
다른 서버와 마찬가지로 In-process 서버는 단일 스레드, 아파트 스레드 또는 자유 스레드일 수 있습니다. 이러한 서버는 해당 클라이언트에서 사용하는 스레딩 모델에 관계없이 모든 OLE 클라이언트에서 사용할 수 있습니다.
스레딩 모델 상호 운용성의 모든 조합은 클라이언트와 In-process 개체 간에 허용됩니다. 클라이언트와 다른 스레딩 모델을 사용하는 in-process 개체 간의 상호 작용은 클라이언트와 Out-of-process 서버 간의 상호 작용과 정확히 비슷합니다. In-Process 서버의 경우 클라이언트 및 In-process 서버의 스레딩 모델이 다른 경우 COM은 클라이언트와 개체 간에 자체적으로 개입해야 합니다.
단일 스레드 모델을 지원하는 In-process 개체가 클라이언트의 여러 스레드에서 동시에 호출되는 경우 COM은 클라이언트 스레드가 개체의 인터페이스에 직접 액세스하도록 허용할 수 없습니다. 개체는 이러한 액세스를 위해 설계되지 않았습니다. 대신 COM은 호출이 동기화되고 개체를 만든 클라이언트 스레드에서만 수행되도록 해야 합니다. 따라서 COM은 클라이언트의 주 아파트에 개체를 만들고 프록시를 사용하여 다른 모든 클라이언트 아파트가 개체에 액세스하도록 요구합니다.
클라이언트의 자유 스레드 아파트(다중 스레드 아파트 모델)가 아파트 스레드를 사용하는 프로세스 내 서버를 만들면, COM은 클라이언트에서 단일 스레드 아파트 모델 "호스트" 스레드를 생성합니다. 이 호스트 스레드는 개체를 만들고 인터페이스 포인터는 클라이언트의 자유 스레드 아파트로 다시 마샬링됩니다. 마찬가지로, 아파트 모델 클라이언트의 단일 스레드 아파트가 자유 스레드 In-process 서버를 만들 때 COM은 자유 스레드 호스트 스레드(개체를 만든 다음 클라이언트 단일 스레드 아파트로 다시 마샬링되는 다중 스레드 아파트)를 스핀업합니다.
메모
일반적으로 In-Process 서버에서 사용자 지정 인터페이스를 디자인하는 경우 COM이 클라이언트 아파트 간의 인터페이스를 마샬링할 수 있도록 마샬링 코드도 제공해야 합니다.
COM은 단일 스레드 DLL에서 제공하는 개체에 대한 액세스를 보호하기 위해 만들어진 동일한 클라이언트 아파트에서 액세스하도록 요구합니다. 또한 모든 DLL 진입점(예: DllGetClassObject 및 DllCanUnloadNow) 및 전역 데이터는 항상 동일한 아파트에서 액세스해야 합니다. COM은 클라이언트의 주 아파트에 이러한 개체를 만들어 주 아파트가 개체의 포인터에 직접 액세스할 수 있도록 합니다. 다른 컴파트먼트에서 오는 호출은 인터스레드 마샬링을 사용하여 프록시에서 주 컴파트먼트의 스텁을 거쳐 개체로 전달됩니다. 이렇게 하면 COM에서 개체에 대한 호출을 동기화할 수 있습니다. 스레드 간 호출은 느리므로 여러 아파트를 지원하도록 이러한 서버를 다시 작성하는 것이 좋습니다.
단일 스레드 인프로세스 서버와 마찬가지로 아파트 모델 DLL에서 제공하는 개체는 생성된 동일한 클라이언트 아파트에서 액세스해야 합니다. 그러나 이 서버에서 제공하는 개체는 클라이언트의 여러 아파트에서 만들 수 있으므로 서버는 다중 스레드 사용을 위해 진입점(예: DllGetClassObject 및 DllCanUnloadNow)을 구현해야 합니다. 예를 들어 클라이언트의 두 아파트가 in-process 개체의 두 인스턴스를 동시에 만들려고 하면 두 아파트 모두에서 DllGetClassObject 동시에 호출할 수 있습니다. DllCanUnloadNow은 코드가 DLL에서 실행되는 동안 DLL이 언로드되지 않도록 해야 합니다.
DLL이 모든 개체를 만들기 위해 클래스 팩터리의 인스턴스를 하나만 제공하는 경우 여러 클라이언트 아파트에서 액세스할 수 있으므로 클래스 팩터리 구현도 다중 스레드 사용을 위해 설계되어야 합니다. DllGetClassObject 호출될 때마다 DLL이 클래스 팩터리의 새 인스턴스를 만드는 경우 클래스 팩터리는 스레드로부터 안전하지 않아도 됩니다.
클래스 팩터리에서 만든 개체는 스레드로부터 안전할 필요가 없습니다. 스레드에서 만든 개체는 항상 해당 스레드를 통해 액세스되고 개체에 대한 모든 호출은 COM에서 동기화됩니다. 이 개체를 만드는 클라이언트의 아파트 모델 아파트는 개체에 대한 직접 포인터를 가져옵니다. 개체가 만들어진 아파트와 다른 클라이언트 아파트는 프록시를 통해 개체에 액세스해야 합니다. 이러한 프록시는 클라이언트가 아파트 간의 인터페이스를 마샬링할 때 만들어집니다.
In-process DLL ThreadingModel 값이 "Both"로 설정된 경우 이 DLL에서 제공하는 개체를 단일 스레드 또는 다중 스레드 클라이언트 아파트에서 직접 만들어서 사용할 수 있습니다(프록시 없이). 그러나 만들어진 아파트 내에서만 직접 사용할 수 있습니다. 개체를 다른 아파트에 전달하려면 개체를 마샬링해야 합니다. DLL 개체는 자체 동기화를 구현해야 하며 동시에 여러 클라이언트 아파트에서 액세스할 수 있습니다.
COM은 프로세스 내 DLL 개체에 대한 자유 스레드 액세스를 위한 성능을 가속화하기 위해 CoCreateFreeThreadedMarshaler 함수를 제공합니다. 이 함수는 In-process 서버 개체를 사용하여 집계할 수 있는 자유 스레드 마샬링 개체를 만듭니다. 동일한 프로세스의 클라이언트 아파트가 다른 아파트의 개체에 액세스해야 하는 경우 자유 스레드 마샬러를 집계하면 클라이언트가 개체의 인터페이스를 다른 아파트로 마샬링할 때 프록시가 아닌 서버 개체에 대한 직접 포인터가 클라이언트에 제공됩니다. 클라이언트는 동기화를 수행할 필요가 없습니다. 이 작업은 동일한 프로세스 내에서만 작동합니다. 표준 마샬링이 다른 프로세스로 전송되는 개체에 대한 참조에 사용됩니다.
자유 스레딩만 지원하는 In-process DLL에서 제공하는 개체는 자유 스레드 개체입니다. 자체 동기화를 구현하고 동시에 여러 클라이언트 스레드에서 액세스할 수 있습니다. 이 서버는 스레드 간의 인터페이스를 마샬링하지 않으므로 클라이언트의 다중 스레드 아파트에서만 이 서버를 직접 만들고 사용할 수 있습니다(프록시 없이). 그것을 만드는 단일 스레드 아파트는 프록시를 통해 액세스합니다.
관련 항목
-
아파트 간 인터페이스 액세스하기