Compartilhar via


Apartamentos Single-Threaded

O uso de apartamentos de thread único (o processo de modelo de apartamento) oferece um paradigma baseado em mensagem para lidar com vários objetos em execução simultaneamente. Ele permite que você escreva um 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 inicializado como um processo de modelo de apartamento e que recupera e despacha mensagens de janela é um thread de apartamento de thread único. Cada thread vive dentro de seu próprio apartamento. Em um apartamento, os ponteiros de interface podem ser passados sem marshaling e, portanto, todos os objetos em um thread de apartamento de thread único se comunicam diretamente.

Um agrupamento lógico de objetos relacionados que todos são executados no mesmo thread e, portanto, devem ter execução síncrona, pode residir 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 no contexto do thread proprietário, portanto, o COM distribuído alterna os threads para você automaticamente quando você chama um proxy.

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

As regras para apartamentos de thread único são simples, mas é importante segui-las com cuidado:

  • Cada objeto deve residir em apenas um thread (dentro de um apartamento de thread único).
  • Inicialize a biblioteca COM para cada thread.
  • Marshal all pointers to objects when pass them between apartments.
  • 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 expedir 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 ThreadingModel valor nomeado sob a chave InprocServer32 no registro. Objetos com reconhecimento de apartamento também devem gravar cuidadosamente os pontos de entrada de DLL. Há considerações especiais que se aplicam aos servidores em processo de threading. Para obter mais informações, consulte In-Process problemas de threading do servidor.

Embora vários objetos possam residir 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 CoInitializeou 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 In-Process Problemas de Threading do Servidor.

Todas as chamadas para um objeto devem ser feitas em seu thread (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 empacotados quando passados entre apartamentos. O COM fornece as duas funções a seguir para esta finalidade:

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

Em geral, o marshaling é realizado automaticamente pelo COM. Por exemplo, ao passar um ponteiro de interface como um parâmetro em uma chamada de método em um proxy para um objeto em outro apartamento ou ao chamar CoCreateInstance, COM faz o marshaling automaticamente. No entanto, em alguns casos especiais, em que o gravador de aplicativos está passando ponteiros de interface entre apartamentos sem usar os mecanismos COM normais, o gravador deve manipular o marshaling 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 deverá chamar CoMarshalInterThreadInterfaceInStream para fazer marshaling da 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 cancelar o registro da interface e obterá de volta um ponteiro para um proxy por meio 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 In-Process Problemas de Threading do Servidor). 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 marshaling e a alternância de thread para o aplicativo.

Para lidar com chamadas de outros processos e apartamentos no mesmo processo, cada apartamento com 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 outros primitivos de sincronização estiverem sendo usados para se comunicar entre threads, a função MsgWaitForMultipleObjects poderá ser usada para aguardar mensagens e eventos de sincronização de thread. A documentação dessa função tem um exemplo desse tipo de loop de combinação.

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 expedir a mensagem, a janela oculta a receberá. Em seguida, 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 sempre que seu apartamento recuperar e expedir 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 de thread único podem implementar IMessageFilter para permitir que eles cancelem chamadas ou recebam mensagens de janela quando necessário.

O objeto poderá ser reentrado se uma de suas implementações de método de interface recuperar e expedir mensagens ou fizer uma chamada ORPC para outro thread, fazendo com que outra chamada seja entregue ao objeto (pelo mesmo apartamento). O OLE não impede a reentrada no mesmo thread, mas pode ajudar a fornecer segurança de thread. Isso é idêntico à maneira como um procedimento de janela pode ser reentrado se recuperar e expedir 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 com thread único permitirá que o primeiro servidor seja reentrado.

acessando interfaces entre apartamentos

escolhendo o modelo de threading

de Apartamentos Multithreaded

problemas de threading do servidor In-Process

processos, threads e apartamentos

Single-Threaded e de Comunicação Multithreaded