1. Introdução
Imagina que threads são pessoas numa fila por sorvete. Tem quem fique tranquilo, e tem quem comece a fazer barulho ("Meu trem sai logo, me deixem passar!"), e o vendedor até escuta todo mundo, mas às vezes atende alguém fora da ordem — por exemplo, uma mãe com uma criança chorando. As prioridades de threads são mais ou menos a mesma ideia.
No C# (na verdade, no .NET) cada thread tem uma prioridade — uma dica pro sistema operacional de quão "importante" é esse thread comparado aos outros. Não é uma regra rígida pra OS (ninguém garante que o thread com maior prioridade vai receber todo o tempo de CPU sempre), mas normalmente threads com prioridade maior recebem mais tempo de processador.
Onde isso é realmente necessário?
- Em interfaces de usuário: por exemplo, atualizar a tela não deve ser travado por cálculos longos com prioridade baixa.
- Em jogos: rendering de frame é mais importante que geração de mapa em background.
- Em tarefas com tempo de resposta crítico: controle de hardware, processamento de sinais etc.
2. Prioridades de threads: como funciona
Em C# é simples mexer com a prioridade do thread — o objeto Thread tem a propriedade Priority. Ela aceita valores do enum ThreadPriority:
| Valor | Descrição |
|---|---|
|
Prioridade mais baixa |
|
Abaixo do normal |
|
Normal (padrão) |
|
Acima do normal |
|
Prioridade mais alta |
Exemplo de código:
using System;
using System.Threading;
class Program
{
static void Main()
{
Thread lowPriorityThread = new Thread(PrintLowPriority);
Thread highPriorityThread = new Thread(PrintHighPriority);
lowPriorityThread.Priority = ThreadPriority.Lowest;
highPriorityThread.Priority = ThreadPriority.Highest;
lowPriorityThread.Start();
highPriorityThread.Start();
}
static void PrintLowPriority()
{
for (int i = 0; i < 10; i++)
{
Console.WriteLine("Baixa prioridade: " + i);
Thread.Sleep(10); // Vamos colocar uma pausa pra ver a diferença
}
}
static void PrintHighPriority()
{
for (int i = 0; i < 10; i++)
{
Console.WriteLine("Alta prioridade: " + i);
Thread.Sleep(10);
}
}
}
Observação
Na prática, especialmente em sistemas modernos com múltiplos núcleos e no ambiente gerenciado do .NET, prioridade é só uma recomendação pro sistema operacional. O SO (e o .NET) tenta dar mais tempo ao thread com prioridade maior, mas não há garantias rígidas. Por exemplo, se um thread monopolizar a CPU com prioridade Highest, a interface pode começar a travar e os outros threads vão sofrer.
3. Mudando prioridades de threads
Threads e prioridades
graph LR
A[Thread 1 - Lowest] -->|Menos tempo de CPU| OS(Sistema Operacional)
B[Thread 2 - Normal] --> OS
C[Thread 3 - Highest] -->|Mais tempo de CPU| OS
OS --> CPU(Processador)
Por que mudar a prioridade de um thread?
Pode parecer que sempre dá pra colocar Highest, mas isso é uma má ideia! Imagina se todos os clientes na loja começassem a exigir "Atende eu primeiro!".
Mude prioridade só quando for justificado:
- Thread de log em background — pode ser BelowNormal ou Lowest.
- Thread que processa entrada do usuário — AboveNormal.
- Tarefas pesadas de CPU que não são críticas no tempo — prioridade baixa.
Dica: Se a aplicação começar a travar, cheque se não tem threads "abusivos" com prioridade alta atrapalhando os outros!
4. Tipos (categorias) de threads no .NET
No C# normalmente distinguimos dois tipos de threads: foreground e background. Curioso, mas "background" não é exatamente "trabalhando silenciosamente"; vamos explicar.
Foreground threads (principais)
- Por padrão, todos os threads que você cria são foreground.
- Enquanto existir pelo menos um foreground thread vivo, a aplicação não termina, mesmo que todo o resto já tenha "morrido".
- Exemplo: o thread principal do programa (Main), e todos os threads criados via new Thread() sem alteração.
Background threads (filhos)
- Se todos os foreground threads terminarem e só ficarem background threads, o processo é finalizado e esses threads são interrompidos sem aviso.
- Usados para tarefas secundárias: logging, envio de métricas em background etc.
- Pra tornar um thread background: defina a propriedade IsBackground como true:
Thread t = new Thread(SomeMethod);
t.IsBackground = true;
t.Start();
Demonstração da diferença
using System;
using System.Threading;
class Program
{
static void Main()
{
Thread backgroundThread = new Thread(() =>
{
for (int i = 0; i < 10; i++)
{
Console.WriteLine("Background thread trabalhando... " + i);
Thread.Sleep(500);
}
Console.WriteLine("O background thread TERMINOU.");
});
backgroundThread.IsBackground = true; // Tornando o thread em background!
backgroundThread.Start();
Console.WriteLine("Main vai encerrar em 1 segundo.");
Thread.Sleep(1000); // Espera 1 segundo
Console.WriteLine("Main terminou. O que vai acontecer com o background thread?");
}
}
O que você vai ver?
Main pode terminar e o background thread vai ser "desligado" no meio do caminho. Isso é útil para tarefas que não devem impedir o fechamento da aplicação.
5. Tipos de threads
ThreadPool (Pool de threads)
- ThreadPool é um mecanismo que gerencia um conjunto de threads pra evitar criar muitos threads.
- É usado quando há muitas tarefas curtas: processamento paralelo de requisições, operações assíncronas.
- Threads do pool são sempre background — eles não impedem o encerramento da aplicação.
Exemplo: executar código no thread pool
using System;
using System.Threading;
class Program
{
static void Main()
{
ThreadPool.QueueUserWorkItem(DoWorkInThreadPool, "tarefa em background");
Console.WriteLine("Main encerrando.");
Thread.Sleep(500);
}
static void DoWorkInThreadPool(object? state)
{
Console.WriteLine("Thread do pool: " + state);
Thread.Sleep(1000); // Vamos tentar dormir
Console.WriteLine("Thread do pool terminou!");
}
}
Fique atento:
Se o Main terminar antes, o thread do pool pode ser interrompido. Se você precisa garantir que termine, use foreground threads ou espere explicitamente.
Evolução da multithreading: tasks, async/await
Em apps modernos raramente se cria threads manualmente (Thread). Normalmente se usa Task e métodos assíncronos. Importante saber:
- Threads do pool e Task normalmente rodam em background threads.
- Na maioria dos casos não é necessário mexer em prioridade — siga o bom senso e as melhores práticas.
6. Dicas úteis
Propriedades e comportamento de threads
| Propriedade | Foreground Thread | Background Thread | ThreadPool Thread |
|---|---|---|---|
|
false por padrão | true | true |
| Encerramento da aplicação | A aplicação NÃO encerra enquanto existir pelo menos um foreground thread vivo | A aplicação encerra se todos os foreground threads tiverem terminado | A aplicação encerra assim que Main terminar |
| Controle | Controle total | Controle total | Sem controle direto |
| Onde usado | Tarefas longas e importantes (por exemplo, servidor de banco de dados) | Tarefas não críticas, operações em background, logging | Tarefas curtas, Task, operações assíncronas |
| Prioridade | Pode ser definida | Pode ser definida | Não é recomendado alterar |
Coisas não óbvias e observações
- Você só pode mudar a prioridade de threads normais (Thread), não de tasks do pool (Task, ThreadPool).
- Threads do pool sempre têm prioridade Normal (não pode mudar).
- Task e async/await são a abordagem mais moderna, onde questões de prioridade e execução em background ficam "nos bastidores".
7. Erros típicos com prioridades e tipos de threads
Erro nº1: abuso de prioridades.
Atribuir Highest ou Lowest pra todo mundo sem motivo não ajuda. Pode quebrar o equilíbrio e piorar a responsividade da aplicação.
Erro nº2: término implícito de background threads.
Se um trabalho importante (por exemplo, salvar dados) roda em um background thread e você não espera ele terminar, corre o risco de perder dados. Background threads são interrompidos automaticamente quando o processo encerra.
Erro nº3: expectativas exageradas sobre prioridade.
A prioridade de thread (Priority) é só uma recomendação ao sistema operacional, não uma garantia de que o thread será executado primeiro.
GO TO FULL VERSION