Processos, threads e apartamentos

Um processo é um conjunto de espaço de memória virtual, código, dados e recursos do sistema. Um thread é um código que deve ser executado em série dentro de um processo. Um processador executa threads, não processos, portanto, cada aplicativo tem pelo menos um processo, e um processo sempre tem pelo menos um thread de execução, conhecido como thread primário. Um processo pode ter vários threads além do thread primário.

Os processos comunicam entre si através de mensagens, utilizando a tecnologia de Chamada de Procedimento Remoto (RPC) da Microsoft para transmitir informações uns aos outros. Não há diferença para o chamador entre uma chamada proveniente de um processo em uma máquina remota e uma chamada proveniente de outro processo na mesma máquina.

Quando um thread começa a ser executado, ele continua até ser morto ou até ser interrompido por um thread com prioridade mais alta (por uma ação do usuário ou pelo agendador de threads do kernel). Cada thread pode executar seções separadas de código, ou vários threads podem executar a mesma seção de código. Os threads que executam o mesmo bloco de código mantêm pilhas separadas. Cada thread em um processo compartilha as variáveis e recursos globais desse processo.

O agendador de threads determina quando e com que frequência executar um thread, de acordo com uma combinação do atributo de classe de prioridade do processo e a prioridade base do thread. Você define o atributo de classe de prioridade de um processo chamando a função deSetPriorityClasse define a prioridade base de um thread com uma chamada para SetThreadPriority.

Os aplicativos multithreaded devem evitar dois problemas de threading: deadlocks e corridas. Um impasse ocorre quando cada thread está esperando que o outro faça algo. O controle de chamada COM ajuda a evitar bloqueios em chamadas entre objetos. Uma condição de corrida ocorre quando um thread termina antes de outro do qual depende, fazendo com que o primeiro use um valor não inicializado porque o segundo ainda não forneceu um válido. O COM disponibiliza algumas funções especificamente concebidas para ajudar a evitar condições de disputa em servidores externos ao processo. (Consulte Auxiliares de implementação do servidor fora de processo.)

O Apartamento e a Arquitetura COM Threading

Embora o COM ofereça suporte ao modelo de thread único por processo predominante antes da introdução de vários threads de execução, você pode escrever código para aproveitar vários threads, resultando em aplicativos mais eficientes, permitindo que um thread seja executado enquanto outro thread aguarda a conclusão de alguma operação demorada.

Observação

Usar vários threads não é garantia de melhor desempenho. Na verdade, como a fatoração de threads é um problema difícil, o uso de vários threads geralmente causa problemas de desempenho. O importante é usar múltiplas linhas de execução apenas se souber muito bem o que está a fazer.

 

Em geral, a forma mais simples de encarar a arquitetura de threading do COM é pensar em todos os objetos COM existentes no processo como estando divididos em grupos chamados apartments. Um objeto COM vive exatamente em um apartamento, no sentido de que seus métodos legalmente podem ser chamados diretamente apenas por um fio que pertence a esse apartamento. Qualquer outro thread que queira chamar o objeto deve passar por um proxy.

Existem dois tipos de apartamentos: apartamentos single-threaded, e apartamentos multithreaded.

Importante

Escolher um modelo de apartamento (STA vs MTA):

Apartamento CoInitializeEx bandeira Utilizar quando
STA (rosca única) COINIT_APARTMENTTHREADED A tua thread tem um ciclo de mensagens (threads da IU), ou estás a utilizar objetos COM que requerem uma bomba de mensagens (controlos ActiveX, objetos Shell, arrastar e largar).
MTA (multithreaded) COINIT_MULTITHREADED O teu thread executa trabalho em segundo plano sem loop de mensagens, e os objetos COM que usas são thread-safe ou ágeis.

Armadilha comum de impasse: Um thread STA deve bombear mensagens (via GetMessage/DispatchMessage ou equivalente). Se um fio STA bloqueia numa primitiva de sincronização (por exemplo, WaitForSingleObject) sem bombear mensagens, chamadas COM recebidas para objetos nesse apartamento nunca serão entregues — causando bloqueios. Use CoWaitForMultipleHandles ou MsgWaitForMultipleObjects em vez de espera bruta num tópico STA.

  • Os apartamentos de thread única consistem em exatamente uma única thread, pelo que todos os objetos COM que residem num apartamento de thread única só podem receber chamadas de método da única thread que pertence a esse apartamento. Todas as chamadas de método para um objeto COM em um apartamento de thread único são sincronizadas com a fila de mensagens do Windows para o thread do apartamento de thread único. Um processo com um único thread de execução é simplesmente um caso especial deste modelo.
  • Os apartamentos multithreaded consistem em uma ou mais threads, de modo que todos os objetos COM que residem num apartamento multithreaded podem receber chamadas de método diretamente de qualquer um dos threads que pertencem aos apartamentos multithreaded. As threads num apartamento multithread utilizam um modelo chamado free-threading. As chamadas para objetos COM em um apartamento multithreaded são sincronizadas pelos próprios objetos.

Observação

Para obter uma descrição da comunicação entre unidades de execução monofio e unidades de execução multifio no mesmo processo, consulte Comunicação monofio e multifio.

 

Um processo pode ter zero ou mais apartamentos single-threaded e zero ou um apartamento multithreaded.

Em um processo, o apartamento principal é o primeiro a ser inicializado. Num processo com uma única thread, este é o único apartamento. Os parâmetros de chamada são organizados entre apartamentos, e o COM lida com a sincronização por meio de mensagens. Se você designar vários threads em um processo para ser free-threaded, todos os threads livres residem em um único apartamento, os parâmetros são passados diretamente para qualquer thread no apartamento e você deve lidar com toda a sincronização. Num processo com multithreading livre e multithreading por apartamento, todas as threads livres estão num único apartamento e todos os outros apartamentos são apartamentos de thread única. Um processo que executa trabalho COM é um conjunto de apartamentos com, no máximo, um apartamento multithread, mas qualquer quantidade de apartamentos de execução única.

Os modelos de threading em COM fornecem o mecanismo para clientes e servidores que usam diferentes arquiteturas de threading trabalharem juntos. Chamadas entre objetos com diferentes modelos de threading em diferentes processos são naturalmente suportadas. Da perspetiva do objeto chamador, todas as chamadas para objetos fora de um processo se comportam de forma idêntica, não importa como o objeto que está sendo chamado é encadeado. Da mesma forma, do ponto de vista do objeto chamado, as chamadas recebidas comportam-se de forma idêntica, independentemente do modelo de threads do chamador.

A interação entre um cliente e um objeto fora do processo é simples, mesmo quando eles usam modelos de threading diferentes, porque o cliente e o objeto estão em processos diferentes. COM, interposto entre o cliente e o servidor, pode fornecer o código para os modelos de threading interoperarem, usando marshaling padrão e RPC. Por exemplo, se um objeto de thread único for chamado simultaneamente por vários clientes de thread livre, as chamadas serão sincronizadas por COM colocando mensagens de janela correspondentes na fila de mensagens do servidor. O apartamento do objeto receberá uma chamada cada vez que recuperar e enviar mensagens. No entanto, alguns cuidados devem ser tomados para garantir que os servidores em processo interajam adequadamente com seus clientes. (Consulte Problemas de processamento por threads do servidor no processo.)

A questão mais importante na programação com um modelo multithreaded é tornar seu thread de código seguro para que as mensagens destinadas a um thread específico vão apenas para esse thread e o acesso aos threads seja protegido.

Para obter mais informações, consulte os seguintes tópicos: