O pool de threads gerenciados
A classe System.Threading.ThreadPool fornece ao seu aplicativo um pool de threads de trabalho gerenciados pelo sistema, permitindo que você se concentre em tarefas de aplicativo, e não no gerenciamento de threads. Se você tiver tarefas curtas que exigem processamento em segundo plano, o pool de threads gerenciados será uma maneira fácil de aproveitar os vários threads. O uso do pool de threads é significativamente mais fácil no Framework 4 e versões posteriores, já que você pode criar objetos Task e Task<TResult> que executam tarefas assíncronas em threads do pool de threads.
O .NET usa threads do pool de threads para muitos fins, incluindo em operações de TPL (Biblioteca de Paralelismo de Tarefas), na conclusão de E/S assíncrona, em retornos de chamada timer, em operações de espera registradas, em chamadas de método assíncrono usando delegados e em conexões de soquete System.Net.
Características do pool de threads
Os threads de pool de threads são threads de segundo plano. Cada thread usa o tamanho da pilha padrão, é executado com prioridade padrão e está no apartamento de vários threads. Depois que um thread do pool conclui sua tarefa, ele é retornado para uma fila de threads em espera. A partir desse momento, ele pode ser reutilizado. Essa reutilização permite que os aplicativos evitem o custo de criação de um novo thread para cada tarefa.
Há apenas um pool de threads por processo.
Exceções em threads do pool de threads
Exceções sem tratamento nos threads do pool de threads encerram o processo. Há três exceções a essa regra:
- Um System.Threading.ThreadAbortException é gerado em um thread do pool de threads porque Thread.Abort foi chamado.
- Um System.AppDomainUnloadedException é gerado em um thread do pool de threads porque o domínio do aplicativo está sendo descarregado.
- O CLR ou um processo de host encerra o thread.
Para saber mais, veja Exceções em threads gerenciados.
Número máximo de threads do pool de threads
O número de operações que podem ser enfileiradas para o pool de threads é limitado apenas pela memória disponível. No entanto, o pool de threads limita o número de threads que podem estar ativos no processo simultaneamente. Se todos os threads do pool de threads estiverem ocupados, itens de trabalho adicionais serão enfileirados até que os threads para os executar se tornem disponíveis. O tamanho padrão do pool de threads de um processo depende de vários fatores, como o tamanho do espaço de endereço virtual. Um processo pode chamar o método ThreadPool.GetMaxThreads para determinar o número de threads.
Você pode controlar o número máximo de threads usando os métodos ThreadPool.GetMaxThreads e ThreadPool.SetMaxThreads.
Observação
O código que hospeda o Common Language Runtime pode definir o tamanho usando o método ICorThreadpool::CorSetMaxThreads
.
Valores mínimos no pool de threads
O pool de threads fornece novos threads de trabalho ou threads de conclusão de E/S sob demanda, até atingir um mínimo especificado para cada categoria. Você pode usar o método ThreadPool.GetMinThreads para obter esses valores mínimos.
Observação
Quando a demanda é baixa, o número real de threads do pool de threads pode ficar abaixo dos valores mínimos.
Quando o mínimo é atingido, o pool de threads pode criar threads adicionais ou aguardar a conclusão de algumas tarefas. O pool de threads cria e destrói threads de trabalho a fim de otimizar a taxa de transferência, que é definida como o número de tarefas concluídas por unidade de tempo. Pouquíssimos threads podem não fazer um uso ideal dos recursos disponíveis, enquanto muitos threads podem aumentar a contenção de recursos.
Cuidado
Use o método ThreadPool.SetMinThreads para aumentar o número mínimo de threads ociosos. No entanto, o aumento desnecessário desses valores pode causar problemas de desempenho. Se muitas tarefas começarem ao mesmo tempo, todas elas podem parecer lentas. Na maioria dos casos, o pool de threads terá um desempenho melhor com seu próprio algoritmo de alocação de threads.
Usando o pool de threads
A maneira mais fácil de usar o pool de threads é usando a TPL (Biblioteca de paralelismo de tarefas). Por padrão, tipos de TPL como Task e Task<TResult> usam threads do pool de threads para executar tarefas.
Você também pode usar o pool de threads chamando ThreadPool.QueueUserWorkItem no código gerenciado (ou ICorThreadpool::CorQueueUserWorkItem
no código não gerenciado) e passando um delegado System.Threading.WaitCallback que representa o método que executa a tarefa.
Outra maneira de usar o pool de threads é enfileirando itens de trabalho relacionados a uma operação de espera usando o método ThreadPool.RegisterWaitForSingleObject e passando um System.Threading.WaitHandle que, quando sinalizado ou após tempo limite, chama o método representado pelo delegado System.Threading.WaitOrTimerCallback. Os threads do pool de thread são usados para invocar métodos de retorno de chamada.
Para os exemplos, verifique as páginas de API referenciadas.
Ignorando as verificações de segurança
O pool de threads também fornece os métodos ThreadPool.UnsafeQueueUserWorkItem e ThreadPool.UnsafeRegisterWaitForSingleObject. Use estes métodos somente quando você tiver certeza de que a pilha do chamador é irrelevante a qualquer verificação de segurança executada durante a execução da tarefa em fila. ThreadPool.QueueUserWorkItem e ThreadPool.RegisterWaitForSingleObject capturam a pilha do chamador, que é mesclada na pilha do thread do pool de threads quando o thread começa a executar uma tarefa. Se uma verificação de segurança for necessária, toda a pilha deverá ser verificada. Embora a verificação forneça segurança, também tem um custo de desempenho.
Quando não usar o thread do pool de threads
Há várias situações nas quais é apropriado criar e gerenciar seus próprios threads em vez de usar os threads do pool de threads:
- Você precisa de um thread em primeiro plano.
- Você precisa que um thread tenha uma prioridade específica.
- Há tarefas que causam o bloqueio do thread por longos períodos. O pool de threads tem um número máximo de threads, portanto, a existência de muitos threads do pool de threads bloqueados pode impedir a inicialização das tarefas.
- Você precisa colocar os threads em um apartamento de thread único. Todos os threads ThreadPool estão no apartamento de vários threads.
- Você precisa ter uma identidade estável associada ao thread, ou dedicar um thread a uma tarefa.