CodeGym /Cursos /C# SELF /Prioridades de threads e tipos de threads

Prioridades de threads e tipos de threads

C# SELF
Nível 55 , Lição 3
Disponível

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
Lowest
Prioridade mais baixa
BelowNormal
Abaixo do normal
Normal
Normal (padrão)
AboveNormal
Acima do normal
Highest
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
IsBackground
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.

Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION