Problemas de threading do servidor em processo

Um servidor em processo não chama CoInitialize, CoInitializeEx ou OleInitialize para marcar seu modelo de threading. Para objetos baseados em DLL com reconhecimento de thread ou em processo, você precisa definir o modelo de threading no Registro. O modelo padrão quando você não especifica um modelo de threading é single-thread-per-process. Para especificar um modelo, adicione o valor ThreadingModel à chave InprocServer32 no registro.

DLLs que oferecem suporte à instanciação de um objeto de classe devem implementar e exportar as funções DllGetClassObject e DllCanUnloadNow. Quando um cliente deseja uma instância da classe que a DLL suporta, uma chamada para CoGetClassObject (diretamente ou por meio de uma chamada para CoCreateInstance) chama DllGetClassObject para obter um ponteiro para seu objeto de classe quando o objeto é implementado em uma DLL. DllGetClassObject deve, portanto, ser capaz de distribuir vários objetos de classe ou um único objeto thread-safe (essencialmente apenas usando InterlockedIncrement/InterlockedDecrement em suas contagens de referência interna).

Como o próprio nome indica, DllCanUnloadNow é chamado para determinar se a DLL que o implementa está em uso, permitindo que o chamador o descarregue com segurança se não estiver. Chamadas para CoFreeUnusedLibraries de qualquer thread sempre roteiam através do thread do apartamento principal para chamar DllCanUnloadNow.

Como outros servidores, os servidores em processo podem ser single-threaded, apartment-threaded ou free-threaded. Esses servidores podem ser usados por qualquer cliente OLE, independentemente do modelo de threading usado por esse cliente.

Todas as combinações de interoperabilidade de modelo de threading são permitidas entre clientes e objetos em processo. A interação entre um cliente e um objeto em processo que usa modelos de threading diferentes é exatamente como a interação entre clientes e servidores fora de processo. Para um servidor em processo, quando o modelo de threading do cliente e do servidor em processo diferem, o COM deve se interpor entre o cliente e o objeto.

Quando um objeto em processo que oferece suporte ao modelo de thread único é chamado simultaneamente por vários threads de um cliente, o COM não pode permitir que os threads do cliente acessem diretamente a interface do objeto, o objeto não foi projetado para esse acesso. Em vez disso, COM deve garantir que as chamadas sejam sincronizadas e feitas somente pelo thread do cliente que criou o objeto. Portanto, COM cria o objeto no apartamento principal do cliente e requer que todos os outros apartamentos do cliente acessem o objeto usando proxies.

Quando um apartamento de thread livre (modelo de apartamento multithreaded) em um cliente cria um servidor em processo de thread de apartamento, o COM gira um thread "host" de modelo de apartamento de thread único no cliente. Esse thread de host criará o objeto e o ponteiro da interface será empacotado de volta para o apartamento de thread livre do cliente. Da mesma forma, quando um apartamento de thread único em um cliente de modelo de apartamento cria um servidor de thread livre em processo, o COM gira um thread de host de thread livre (apartamento multithreaded no qual o objeto será criado e, em seguida, empacotado de volta para o apartamento de thread único do cliente).

Observação

Em geral, se você projetar uma interface personalizada em um servidor em processo, também deverá fornecer o código de empacotamento para que o COM possa organizar a interface entre os apartamentos do cliente.

 

COM ajuda a proteger o acesso a objetos fornecidos por uma DLL de thread único, exigindo acesso do mesmo apartamento cliente em que eles foram criados. Além disso, todos os pontos de entrada da DLL (como DllGetClassObject e DllCanUnloadNow) e dados globais devem sempre ser acessados pelo mesmo apartamento. COM cria tais objetos no apartamento principal do cliente, dando ao apartamento principal acesso direto aos ponteiros do objeto. As chamadas dos outros apartamentos usam o marshaling interthread para ir do proxy para o stub no apartamento principal e, em seguida, para o objeto. Isso permite que COM sincronize chamadas para o objeto. As chamadas interthread são lentas, por isso é recomendável que esses servidores sejam reescritos para oferecer suporte a vários apartamentos.

Como um servidor em processo de thread único, um objeto fornecido por uma DLL de modelo de apartamento deve ser acessado pelo mesmo apartamento cliente a partir do qual foi criado. No entanto, os objetos fornecidos por esse servidor podem ser criados em vários apartamentos do cliente, portanto, o servidor deve implementar seus pontos de entrada (como DllGetClassObject e DllCanUnloadNow) para uso multithreaded. Por exemplo, se dois apartamentos de um cliente tentarem criar duas instâncias do objeto em processo simultaneamente, DllGetClassObject poderá ser chamado simultaneamente por ambos os apartamentos. DllCanUnloadNow deve ser gravado para que a DLL não descarregue enquanto o código ainda está sendo executado na DLL.

Se a DLL fornecer apenas uma instância da fábrica de classes para criar todos os objetos, a implementação de fábrica de classes também deverá ser projetada para uso multithread, pois será acessada por vários apartamentos de cliente. Se a DLL cria uma nova instância da fábrica de classe cada vez que DllGetClassObject é chamado, a fábrica de classe não precisa ser thread-safe.

Os objetos criados pela fábrica de classes não precisam ser thread-safe. Uma vez criado por um thread, o objeto é sempre acessado através desse thread e todas as chamadas para o objeto são sincronizadas por COM. O apartamento modelo de apartamento de um cliente que cria esse objeto receberá um ponteiro direto para o objeto. Os apartamentos do cliente que são diferentes do apartamento em que o objeto foi criado devem acessar o objeto por meio de proxies. Esses proxies são criados quando o cliente faz a interface entre seus apartamentos.

Quando um valor ThreadingModel de DLL em processo é definido como "Both", um objeto fornecido por essa DLL pode ser criado e usado diretamente (sem um proxy) em apartamentos de cliente de thread único ou multithread. No entanto, ele pode ser usado diretamente apenas dentro do apartamento em que foi criado. Para dar o objeto a qualquer outro apartamento, o objeto deve ser marshled. O objeto DLL deve implementar sua própria sincronização e pode ser acessado por vários apartamentos cliente ao mesmo tempo.

Para acelerar o desempenho para acesso de thread livre a objetos DLL em processo, o COM fornece a função CoCreateFreeThreadedMarshaler. Essa função cria um objeto de empacotamento de thread livre que pode ser agregado com um objeto de servidor em processo. Quando um apartamento cliente no mesmo processo precisa de acesso a um objeto em outro apartamento, a agregação do empacotador de thread livre fornece ao cliente um ponteiro direto para o objeto do servidor, em vez de para um proxy, quando o cliente marshals a interface do objeto para um apartamento diferente. O cliente não precisa fazer nenhuma sincronização. Isso funciona apenas dentro do mesmo processo; O empacotamento padrão é usado para uma referência ao objeto que é enviado para outro processo.

Um objeto fornecido por uma DLL em processo que oferece suporte apenas a threading livre é um objeto de thread livre. Ele implementa sua própria sincronização e pode ser acessado por vários threads de cliente ao mesmo tempo. Este servidor não marshal interfaces entre threads, de modo que este servidor pode ser criado e usado diretamente (sem um proxy) apenas por apartamentos multithreaded em um cliente. Os apartamentos single-threaded que o criarem irão acessá-lo através de um proxy.

Acessando interfaces entre apartamentos

Escolhendo o modelo de threading

Apartamentos Multithreaded

Processos, threads e apartamentos

Comunicação Single-Threaded e Multithreaded

Apartamentos Single-Threaded