Agendamento de threads

Cada thread tem uma prioridade de thread atribuída a ele. Os threads criados dentro do Common Language Runtime recebem inicialmente a prioridade de ThreadPriority.Normal. Os threads criados fora do tempo de execução mantêm a prioridade que tinham antes de entrarem no ambiente gerenciado. Você pode obter ou definir a prioridade de qualquer thread com a Thread.Priority propriedade.

Os threads são agendados para execução com base em sua prioridade. Embora os threads estejam sendo executados dentro do tempo de execução, todos os threads recebem fatias de tempo do processador pelo sistema operacional. Os detalhes do algoritmo de agendamento usado para determinar a ordem na qual os threads são executados variam de acordo com cada sistema operacional. Em alguns sistemas operacionais, o thread com a prioridade mais alta (daqueles threads que podem ser executados) é sempre agendado para ser executado primeiro. Se vários threads com a mesma prioridade estiverem disponíveis, o agendador percorre os threads nessa prioridade, dando a cada thread uma fatia de tempo fixa na qual executar. Enquanto um thread com uma prioridade mais alta estiver disponível para execução, threads de prioridade mais baixa não poderão ser executados. Quando não há mais threads executáveis em uma determinada prioridade, o agendador se move para a próxima prioridade inferior e agenda os threads nessa prioridade para execução. Se um thread de prioridade mais alta se tornar executável, o thread de prioridade mais baixa será antecipado e o thread de prioridade mais alta poderá ser executado novamente. Além de tudo isso, o sistema operacional também pode ajustar as prioridades de thread dinamicamente à medida que a interface do usuário de um aplicativo é movida entre o primeiro plano e o plano de fundo. Outros sistemas operacionais podem optar por usar um algoritmo de agendamento diferente.

Exemplo

Aqui está um exemplo da execução de 9 threads em todos os 5 níveis de prioridade da Thread.Priority enumeração onde os últimos 5 estão no nível de prioridade mais alto. Além disso, temos suporte de retorno de chamada do artigo anterior que, neste contexto, demonstra que a ordem de inicialização e priorização de thread nem sempre pode ser refletida no código subsequente nem na ordem de início das execuções de processo. Ou seja, vemos aqui a natureza paralela da execução de código e demonstração de fatias de tempo de processador atribuídas pelo sistema operacional para cada thread. Isso destaca a influência e o controle do ambiente no qual os threads estão sendo executados. Dito isso, certamente vemos que os threads de maior prioridade são, de fato, prioridade na execução.

O código a seguir produzirá resultados arbitrários em cada execução. No entanto, padrões de sequência comuns de prioridades sendo executadas podem ser observados depois de executar o código várias vezes e analisar as saídas.

namespace snippets;

public class SchedulingThreads
{
    public void RunMultipleThreadsOnDifferentPriorities()
    {
        var threadsList = new List<Thread>(9);

        // Initialize 9 threads. 5 with Highest priority, and the first 4 from Lowest to Normal range.
        for (int i = 0; i < 9; i++)
        {
            var thread = new Thread(() => { new ThreadWithCallback(Callback).Process(); });

            if (i > 3)
                thread.Priority = ThreadPriority.Highest;
            else
                thread.Priority = (ThreadPriority)i;

            threadsList.Add(thread);
        }

        threadsList.ForEach(thread => thread.Start());
    }

    public void Callback(ThreadPriority threadPriority)
    {
        Console.WriteLine($"Callback in {threadPriority} priority. \t\t ThreadId: {Thread.CurrentThread.ManagedThreadId}.");
    }

    public class ThreadWithCallback
    {
        public ThreadWithCallback(Action<ThreadPriority> callback)
        {
            this.callback = callback;
        }

        public Action<ThreadPriority> callback;

        public void Process()
        {
            Console.WriteLine($"Entered process in {Thread.CurrentThread.Priority} priority.  \t\t ThreadId: {Thread.CurrentThread.ManagedThreadId}.");
            Thread.Sleep(1000);
            Console.WriteLine($"Finished process in {Thread.CurrentThread.Priority} priority. \t\t ThreadId: {Thread.CurrentThread.ManagedThreadId}.");

            if (callback != null)
            {
                callback(Thread.CurrentThread.Priority);
            }
        }
    }

    // The example displays the output like the following:
    //      Entered process in Highest priority.             ThreadId: 9.
    //      Entered process in Highest priority.             ThreadId: 12.
    //      Entered process in Normal priority.              ThreadId: 6.
    //      Entered process in BelowNormal priority.         ThreadId: 5.
    //      Entered process in Lowest priority.              ThreadId: 4.
    //      Entered process in AboveNormal priority.         ThreadId: 7.
    //      Entered process in Highest priority.             ThreadId: 11.
    //      Entered process in Highest priority.             ThreadId: 10.
    //      Entered process in Highest priority.             ThreadId: 8.
    //      Finished process in Highest priority.            ThreadId: 9.
    //      Finished process in Highest priority.            ThreadId: 12.
    //      Finished process in Highest priority.            ThreadId: 8.
    //      Finished process in Highest priority.            ThreadId: 10.
    //      Callback in Highest priority.                    ThreadId: 10.
    //      Finished process in AboveNormal priority.        ThreadId: 7.
    //      Callback in AboveNormal priority.                ThreadId: 7.
    //      Finished process in Lowest priority.             ThreadId: 4.
    //      Callback in Lowest priority.                     ThreadId: 4.
    //      Finished process in Normal priority.             ThreadId: 6.
    //      Callback in Highest priority.                    ThreadId: 9.
    //      Callback in Highest priority.                    ThreadId: 8.
    //      Callback in Highest priority.                    ThreadId: 12.
    //      Finished process in Highest priority.            ThreadId: 11.
    //      Callback in Highest priority.                    ThreadId: 11.
    //      Callback in Normal priority.                     ThreadId: 6.
    //      Finished process in BelowNormal priority.        ThreadId: 5.
    //      Callback in BelowNormal priority.                ThreadId: 5.
}

Consulte também