In-Process 서버 스레딩 문제

In-process 서버는 CoInitialize, CoInitializeEx 또는 OleInitialize 를 호출하여 스레딩 모델을 표시하지 않습니다. 스레드 인식 DLL 기반 또는 In-process 개체의 경우 레지스트리에서 스레딩 모델을 설정해야 합니다. 스레딩 모델을 지정하지 않는 경우 기본 모델은 프로세스당 단일 스레드입니다. 모델을 지정하려면 ThreadingModel 값을 레지스트리의 InprocServer32 키에 추가합니다.

클래스 개체의 인스턴스화를 지원하는 DLL은 DllGetClassObjectDllCanUnloadNow 함수를 구현하고 내보내야 합니다. 클라이언트가 DLL에서 지원하는 클래스의 instance 원할 때 CoGetClassObject에 대한 호출(직접 또는 CoCreateInstance 호출을 통해)은 DLL에서 개체가 구현될 때 해당 클래스 개체에 대한 포인터를 가져오기 위해 DllGetClassObject를 호출합니다. 따라서 DllGetClassObject는 여러 클래스 개체 또는 단일 스레드로부터 안전한 개체를 제공할 수 있어야 합니다(기본적으로 내부 참조 수에 InterlockedIncrementInterlockedDecrement/ 사용).

이름에서 알 수 있듯이 DllCanUnloadNow는 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은 클라이언트의 기본 아파트에 개체를 만들고 프록시를 사용하여 다른 모든 클라이언트 아파트가 개체에 액세스하도록 요구합니다.

클라이언트의 자유 스레드 아파트(다중 스레드 아파트 모델)가 아파트 스레드 In-Process 서버를 만들면 COM은 클라이언트에서 단일 스레드 아파트 모델 "호스트" 스레드를 스핀업합니다. 이 호스트 스레드는 개체를 만들고 인터페이스 포인터는 클라이언트의 자유 스레드 아파트로 다시 마샬링됩니다. 마찬가지로 아파트 모델 클라이언트의 단일 스레드 아파트가 자유 스레드 in-process 서버를 만들 때 COM은 자유 스레드 호스트 스레드(개체가 만들어지고 클라이언트 단일 스레드 아파트로 다시 마샬링되는 다중 스레드 아파트)를 스핀업합니다.

참고

일반적으로 In-Process 서버에서 사용자 지정 인터페이스를 디자인하는 경우 COM이 클라이언트 아파트 간에 인터페이스를 마샬링할 수 있도록 마샬링 코드도 제공해야 합니다.

 

COM은 단일 스레드 DLL이 만든 동일한 클라이언트 아파트에서 액세스하도록 요구하여 단일 스레드 DLL에서 제공하는 개체에 대한 액세스를 보호하는 데 도움이 됩니다. 또한 모든 DLL 진입점(예: DllGetClassObjectDllCanUnloadNow) 및 전역 데이터는 항상 동일한 아파트에서 액세스해야 합니다. COM은 클라이언트의 기본 아파트에 이러한 개체를 만들어 기본 아파트가 개체의 포인터에 직접 액세스할 수 있도록 합니다. 다른 아파트의 호출은 스레드 간 마샬링을 사용하여 프록시에서 기본 아파트의 스텁으로 이동한 다음 개체로 이동합니다. 이렇게 하면 COM에서 개체에 대한 호출을 동기화할 수 있습니다. 스레드 간 호출이 느리므로 여러 아파트를 지원하도록 이러한 서버를 다시 작성하는 것이 좋습니다.

단일 스레드 In-Process 서버와 마찬가지로 아파트 모델 DLL에서 제공하는 개체는 생성된 동일한 클라이언트 아파트에서 액세스해야 합니다. 그러나 이 서버에서 제공하는 개체는 클라이언트의 여러 아파트에서 만들 수 있으므로 서버는 다중 스레드 사용을 위해 진입점(예: DllGetClassObjectDllCanUnloadNow)을 구현해야 합니다. 예를 들어 클라이언트의 두 아파트가 In-process 개체의 두 인스턴스를 동시에 만들려고 하면 두 아파트에서 DllGetClassObject 를 동시에 호출할 수 있습니다. DLL에서 코드가 실행되는 동안 DLL이 언로드되지 않도록 DllCanUnloadNow를 작성해야 합니다.

DLL이 모든 개체를 만들기 위해 클래스 팩터리의 instance 하나만 제공하는 경우 여러 클라이언트 아파트에서 액세스할 수 있으므로 클래스 팩터리 구현도 다중 스레드 사용을 위해 설계되어야 합니다. DLL이 DllGetClassObject를 호출할 때마다 클래스 팩터리의 새 instance 만드는 경우 클래스 팩터리는 스레드로부터 안전할 필요가 없습니다.

클래스 팩터리에서 만든 개체는 스레드로부터 안전할 필요가 없습니다. 스레드에서 만든 개체는 항상 해당 스레드를 통해 액세스되고 개체에 대한 모든 호출은 COM에서 동기화됩니다. 이 개체를 만드는 클라이언트의 아파트 모델 아파트는 개체에 대한 직접 포인터를 가져옵니다. 개체가 만들어진 아파트와 다른 클라이언트 아파트는 프록시를 통해 개체에 액세스해야 합니다. 이러한 프록시는 클라이언트가 아파트 간의 인터페이스를 마샬링할 때 만들어집니다.

In-process DLL ThreadingModel 값이 "Both"로 설정된 경우 이 DLL에서 제공하는 개체를 단일 스레드 또는 다중 스레드 클라이언트 아파트에서 직접 만들고 사용할 수 있습니다(프록시 없이). 그러나 만들어진 아파트 내에서만 직접 사용할 수 있습니다. 개체를 다른 아파트에 제공하려면 개체를 마샬링해야 합니다. DLL 개체는 자체 동기화를 구현해야 하며 동시에 여러 클라이언트 아파트에서 액세스할 수 있습니다.

COM은 프로세스 내 DLL 개체에 대한 자유 스레드 액세스를 위한 성능을 가속화하기 위해 CoCreateFreeThreadedMarshaler 함수를 제공합니다. 이 함수는 In-process 서버 개체를 사용하여 집계할 수 있는 자유 스레드 마샬링 개체를 만듭니다. 동일한 프로세스의 클라이언트 아파트가 다른 아파트의 개체에 액세스해야 하는 경우 자유 스레드 마샬러를 집계하면 클라이언트가 개체의 인터페이스를 다른 아파트로 마샬링할 때 프록시가 아닌 서버 개체에 대한 직접 포인터가 클라이언트에 제공됩니다. 클라이언트는 동기화를 수행할 필요가 없습니다. 이는 동일한 프로세스 내에서만 작동합니다. 표준 마샬링 은 다른 프로세스로 전송되는 개체에 대한 참조에 사용됩니다.

여유 스레딩만 지원하는 In-process DLL에서 제공하는 개체는 자유 스레드 개체입니다. 자체 동기화를 구현하고 동시에 여러 클라이언트 스레드에서 액세스할 수 있습니다. 이 서버는 스레드 간의 인터페이스를 마샬링하지 않으므로 클라이언트의 다중 스레드 아파트에서만 이 서버를 직접 만들고 사용할 수 있습니다(프록시 없이). 그것을 만드는 단일 스레드 아파트는 프록시를 통해 액세스합니다.

아파트 간 인터페이스 액세스

스레딩 모델 선택

다중 스레드 아파트먼트

프로세스, 스레드 및 아파트

단일 스레드 및 다중 스레드 통신

단일 스레드 아파트먼트