Threading gerenciado e não gerenciado no Windows
O gerenciamento de todos os threads, inclusive de threads criados pelo Common Language Runtime e fora do tempo de execução que ingressam no ambiente gerenciado para executar o código, é feito pela classe Thread. O runtime monitora todos os threads de seu processo que já executaram o código no ambiente de execução gerenciado. Ele não rastreia nenhum outro thread. Os threads podem ingressar no ambiente de execução gerenciado por meio da interoperabilidade COM (porque o runtime expõe os objetos gerenciados como objetos COM ao mundo não gerenciado), da função COM DllGetClassObject e da invocação de plataforma.
Quando um thread não gerenciado, por exemplo, um COM Callable Wrapper, ingressa no runtime, o sistema verifica o repositório de threads locais do thread em questão para procurar um objeto Thread interno gerenciado. Caso o sistema encontre um thread desse tipo, o runtime fica ciente sobre ele. Caso contrário, o runtime compila um novo objeto Thread e o instala no repositório de threads locais do thread em questão.
No threading gerenciado, Thread.GetHashCode é a identificação estável do thread gerenciado. Durante seu tempo de vida, o thread não colidirá com os valores de outros threads, independente do domínio do aplicativo para o qual você obtém esse valor.
Mapeando do threading do Win32 para o threading gerenciado
A tabela a seguir mapeia os elementos do threading do Win32 com seu equivalente aproximado no runtime. Esse mapeamento não representa uma funcionalidade idêntica. Por exemplo, TerminateThread não executa cláusulas finally nem libera recursos, e não é possível evitá-lo. No entanto, Thread.Abort executa todo o código de reversão, recupera todos os recursos e pode ser negado com ResetAbort. Leia a documentação com atenção e não faça deduções sobre a funcionalidade.
No Win32 | No Common Language Runtime |
---|---|
CreateThread | Combinação de Thread e ThreadStart |
TerminateThread | Thread.Abort |
SuspendThread | Thread.Suspend |
ResumeThread | Thread.Resume |
Modo de suspensão | Thread.Sleep |
WaitForSingleObject no identificador de thread | Thread.Join |
ExitThread | Sem equivalente |
GetCurrentThread | Thread.CurrentThread |
SetThreadPriority | Thread.Priority |
Sem equivalente | Thread.Name |
Sem equivalente | Thread.IsBackground |
Próximo a CoInitializeEx (OLE32.DLL) | Thread.ApartmentState |
Threads gerenciados e apartments COM
Um thread gerenciado pode ser marcado para indicar que ele irá hospedar um single-threaded apartament ou um multithread apartament. (Para obter mais informações sobre a arquitetura de threading COM, consulte Processos, Threads e Apartments.) Os métodos GetApartmentState, SetApartmentState e TrySetApartmentState da classe Thread retornam e atribuem o estado de apartament de um thread. Se o estado não tiver sido definido, GetApartmentState retorna ApartmentState.Unknown.
Você só pode definir uma propriedade quando o thread estiver no estado ThreadState.Unstarted; ela só pode ser definida uma vez por thread.
Se o estado do apartment não for definido antes do início do thread, o thread será iniciado como MTA (multithreaded apartment). O thread finalizador e todos os threads controlados por ThreadPool são do tipo MTA.
Importante
No caso do código de inicialização do aplicativo, a única forma de controlar o estado do apartment é aplicar MTAThreadAttribute ou STAThreadAttribute ao procedimento do ponto de entrada.
Os objetos gerenciados que são expostos ao COM comportam-se como se houvesse um marshaler de thread livre agregado a eles. Em outras palavras, eles podem ser chamados por qualquer apartment COM no modo de threading livre. Os únicos objetos gerenciados que não apresentam esse comportamento são os objetos derivados de ServicedComponent ou StandardOleMarshalObject.
No mundo gerenciado, não há suporte a SynchronizationAttribute, a menos que você use contextos e instâncias gerenciadas vinculadas a um contexto. Se você estiver usando o Enterprise Services, seu objeto deve ser derivado de ServicedComponent (que, por sua vez, é derivado de ContextBoundObject).
Quando o código gerenciado chama objetos COM, ele sempre segue as regras COM. Em outras palavras, ele faz a chamada por meio de proxies de apartment COM e COM+ 1.0 context wrappers, como ditado por OLE32.
Problemas de bloqueio
Se um thread fizer uma chamada não gerenciada no sistema operacional que bloqueou o thread no código não gerenciado, o runtime não assumirá o controle para Thread.Interrupt nem Thread.Abort. No caso do Thread.Abort, o runtime marca o thread como Abortar e assume o controle dele quando esse ingressa no código gerenciado. Recomendamos que você use bloqueios gerenciados, em vez de não gerenciados. WaitHandle.WaitOne, WaitHandle.WaitAny, WaitHandle.WaitAll, Monitor.Enter, Monitor.TryEnter, Thread.Join, GC.WaitForPendingFinalizers, entre outros, respondem a Thread.Interrupt e a Thread.Abort. Além disso, se seu thread estiver em um single-threaded apartment, todas essas operações de bloqueio gerenciado enviarão mensagens para seu apartment enquanto o thread estiver bloqueado.
Threads e fibras
Modelo de threading do .NET não é compatível com fibras. Você não deve chamar nenhuma função não gerenciada implementada usando fibras. Essas chamadas podem resultar em uma falha de runtime do .NET.