Apartamentos Single-Threaded

O uso de apartamentos de thread único (o processo de modelo de apartamento) oferece um paradigma baseado em mensagens para lidar com vários objetos em execução simultaneamente. Ele permite que você escreva código mais eficiente, permitindo que um thread, enquanto aguarda a conclusão de alguma operação demorada, permita que outro thread seja executado.

Cada thread em um processo que é inicializado como um processo de modelo de apartamento e que recupera e despacha mensagens de janela, é um thread de apartamento de thread único. Cada fio vive dentro de seu próprio apartamento. Dentro de um apartamento, os ponteiros de interface podem ser passados sem empacotamento e, portanto, todos os objetos em um único thread de apartamento se comunicam diretamente.

Um agrupamento lógico de objetos relacionados que todos executam no mesmo thread e, portanto, devem ter execução síncrona, pode viver no mesmo thread de apartamento de thread único. No entanto, um objeto de modelo de apartamento não pode residir em mais de um thread. As chamadas para objetos em outros threads devem ser feitas dentro do contexto do thread proprietário, portanto, o COM distribuído alterna threads para você automaticamente quando você chama um proxy.

Os modelos interprocesso e interthread são semelhantes. Quando é necessário passar um ponteiro de interface para um objeto em outro apartamento (em outro thread) dentro do mesmo processo, você usa o mesmo modelo de empacotamento que objetos em processos diferentes usam para passar ponteiros através dos limites do processo. Ao obter um ponteiro para o objeto marshaling padrão, você pode marshal ponteiros de interface através de limites de thread (entre apartamentos) da mesma forma que faz entre processos. (Os ponteiros de interface devem ser marshaled quando passados entre apartamentos.)

As regras para apartamentos single-threaded são simples, mas é importante segui-las cuidadosamente:

  • Cada objeto deve viver em apenas um thread (dentro de um apartamento de thread único).
  • Inicialize a biblioteca COM para cada thread.
  • Marechal todos os ponteiros para objetos ao passá-los entre apartamentos.
  • Cada apartamento de thread único deve ter um loop de mensagem para lidar com chamadas de outros processos e apartamentos dentro do mesmo processo. Apartamentos de thread único sem objetos (somente cliente) também precisam de um loop de mensagem para despachar as mensagens de transmissão que alguns aplicativos usam.
  • Objetos baseados em DLL ou em processo não chamam as funções de inicialização COM; em vez disso, eles registram seu modelo de threading com o valor nomeado ThreadingModel sob a chave InprocServer32 no registro. Os objetos com reconhecimento de apartamento também devem gravar pontos de entrada DLL cuidadosamente. Há considerações especiais que se aplicam ao threading de servidores em processo. Para obter mais informações, consulte Problemas de threading no servidor em processo.

Embora vários objetos possam viver em um único thread, nenhum objeto de modelo de apartamento pode viver em mais de um thread.

Cada thread de um processo de cliente ou servidor fora de processo deve chamar CoInitialize ou chamar CoInitializeEx e especificar COINIT_APARTMENTTHREADED para o parâmetro dwCoInit. O apartamento principal é o thread que chama CoInitializeEx primeiro. Para obter informações sobre servidores em processo, consulte Problemas de threading no servidor em processo.

Todas as chamadas para um objeto devem ser feitas em seu fio (dentro de seu apartamento). É proibido chamar um objeto diretamente de outro thread; Usar objetos dessa maneira de thread livre pode causar problemas para aplicativos. A implicação dessa regra é que todos os ponteiros para objetos devem ser marshaled quando passados entre apartamentos. COM fornece as seguintes duas funções para este fim:

Essas funções encapsulam chamadas para funções CoMarshalInterface e CoUnmarshalInterface, que exigem o uso do sinalizador MSHCTX_INPROC.

Em geral, o marshaling é realizado automaticamente por COM. Por exemplo, ao passar um ponteiro de interface como parâmetro em uma chamada de método em um proxy para um objeto em outro apartamento, ou ao chamar CoCreateInstance, o COM faz o empacotamento automaticamente. No entanto, em alguns casos especiais, onde o gravador de aplicativos está passando ponteiros de interface entre apartamentos sem usar os mecanismos COM normais, o gravador deve manipular o empacotamento manualmente.

Se um apartamento (Apartamento 1) em um processo tiver um ponteiro de interface e outro apartamento (Apartamento 2) exigir seu uso, o Apartamento 1 deve chamar CoMarshalInterThreadInterfaceInStream para organizar a interface. O fluxo criado por essa função é thread-safe e deve ser armazenado em uma variável acessível pelo Apartamento 2. O Apartamento 2 deve passar esse fluxo para CoGetInterfaceAndReleaseStream para desmarcar a interface e receberá de volta um ponteiro para um proxy através do qual ele pode acessar a interface. O apartamento principal deve permanecer ativo até que o cliente tenha concluído todo o trabalho COM (porque alguns objetos em processo são carregados no apartamento principal, conforme descrito em Problemas de threading do servidor em processo). Depois que um objeto é passado entre threads dessa maneira, é muito fácil passar ponteiros de interface como parâmetros. Dessa forma, o COM distribuído faz o empacotamento e a troca de thread para o aplicativo.

Para lidar com chamadas de outros processos e apartamentos dentro do mesmo processo, cada apartamento de thread único deve ter um loop de mensagem. Isso significa que a função de trabalho do thread deve ter um loop GetMessage/DispatchMessage. Se outras primitivas de sincronização estiverem sendo usadas para se comunicar entre threads, a função MsgWaitForMultipleObjects poderá ser usada para aguardar mensagens e eventos de sincronização de threads. A documentação para essa função tem um exemplo desse tipo de loop de combinação.

COM cria uma janela oculta usando a classe do Windows "OleMainThreadWndClass" em cada apartamento de thread único. Uma chamada para um objeto é recebida como uma mensagem de janela para essa janela oculta. Quando o apartamento do objeto recuperar e enviar a mensagem, a janela oculta a receberá. O procedimento de janela chamará o método de interface correspondente do objeto.

Quando vários clientes chamam um objeto, as chamadas são enfileiradas na fila de mensagens e o objeto receberá uma chamada cada vez que seu apartamento recuperar e despachar mensagens. Como as chamadas são sincronizadas por COM e as chamadas são sempre entregues pelo thread que pertence ao apartamento do objeto, as implementações de interface do objeto não precisam fornecer sincronização. Os apartamentos single-threaded podem implementar o IMessageFilter para permitir que cancelem chamadas ou recebam mensagens de janela quando necessário.

O objeto pode ser reinserido se uma de suas implementações de método de interface recupera e despacha mensagens ou faz uma chamada ORPC para outro thread, fazendo com que outra chamada seja entregue ao objeto (pelo mesmo apartamento). OLE não impede a reentrância no mesmo thread, mas pode ajudar a fornecer segurança de thread. Isso é idêntico à maneira pela qual um procedimento de janela pode ser reinserido se ele recuperar e despachar mensagens durante o processamento de uma mensagem. No entanto, chamar um servidor de apartamento de thread único fora de processo que chama outro servidor de apartamento de thread único permitirá que o primeiro servidor seja reinserido.

Acessando interfaces entre apartamentos

Escolhendo o modelo de threading

Apartamentos Multithreaded

Problemas de threading do servidor em processo

Processos, threads e apartamentos

Comunicação Single-Threaded e Multithreaded