Partilhar via


Apartamentos em Single-Threaded

O uso de apartamentos single-threaded (o processo de modelo de apartamento) oferece um paradigma baseado em mensagens para lidar com vários objetos em execução simultânea. 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 do seu próprio apartamento. Dentro de 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 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 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 de empacotamento padrão, você pode organizar ponteiros de interface através dos 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 fio (dentro de um apartamento de rosca única).
  • 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 mensagens 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 mensagens para enviar as mensagens de difusã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 named-value sob a chave InprocServer32 no registro. Objetos com reconhecimento de apartamento também devem gravar pontos de entrada DLL com cuidado. Há considerações especiais que se aplicam ao threading de servidores em processo. Para obter mais informações, consulte In-Process Server Threading Issues.

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

Cada thread de um processo cliente ou servidor fora do processo deve chamar CoInitializeou chamar CoInitializeEx e especificar COINIT_APARTMENTTHREADED para o parâmetro dwCoInit. O apartamento principal é o tópico que chama CoInitializeEx primeiro. Para obter informações sobre servidores em processo, consulte In-Process Server Threading Issues.

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

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, o COM faz o marshaling 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 lidar com o marshaling manualmente.

Se um apartamento (Apartamento 1) em um processo tem um ponteiro de interface e outro apartamento (Apartamento 2) requer seu uso, o Apartamento 1 deve chamar CoMarshalInterThreadInterfaceInStream para organizar a interface. O fluxo que é criado por esta função é thread-safe e deve ser armazenado em uma variável que é acessível pelo Apartamento 2. O Apartment 2 deve passar esse fluxo para CoGetInterfaceAndReleaseStream para desmarshal 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 vivo 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 Server Threading Issues). Depois que um objeto foi 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 comutação de threads 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 mensagens. 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çãoMsgWaitForMultipleObjects poderá ser usada para aguardar mensagens e eventos de sincronização de threads. A documentação para esta função tem um exemplo deste 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 recupera e envia 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 enviar 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 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 recuperar e despachar mensagens ou fizer uma chamada ORPC para outro thread, fazendo com que outra chamada seja entregue ao objeto (pelo mesmo apartamento). OLE não impede 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 recuperar e enviar 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.

Acesso a interfaces entre apartamentos

Escolhendo o modelo de threading

Apartamentos Multithreaded

Problemas de threading do In-Process Server

Processos, Threads e Apartamentos

Single-Threaded e Comunicação Multithreaded