Agendando threads
Cada thread tem uma prioridade atribuída. Os threads criados dentro do Common Language Runtime recebem inicialmente a prioridade ThreadPriority.Normal. Threads criados fora do Runtime retêm a prioridade que tinham antes de entrarem no ambiente gerenciado. Você pode obter ou definir a prioridade de qualquer thread com a propriedade Thread.Priority.
Threads são agendados para execução com base em sua prioridade. Mesmo que os threads sejam executados no Runtime, todos são atribuídos frações 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 (dos threads que podem ser executados) sempre é programada para ser executado primeiro. Se vários threads com a mesma prioridade estiverem disponíveis, o agendador percorre os threads com essa prioridade, fornecendo uma fração de tempo fixa na qual executar cada thread. Enquanto houver um thread com uma prioridade mais alta estiver disponível para execução, threads com prioridades inferiores não serão executados. Quando não houver mais nenhum threads executável com determinada prioridade, o agendador passará para a próxima prioridade mais alta e agendará a execução dos threads com essa prioridade. Se um thread de prioridade mais alta se tornar executável, o thread de prioridade mais baixo será ignorado e o thread de prioridade mais alta poderá ser executado novamente. Além disso, o sistema operacional também poderá ajustar as prioridades de threads dinamicamente conforme a interface de usuário do aplicativo é movida entre o primeiro e segundo plano. Outros sistemas operacionais podem optar por usar um algoritmo de programação diferente.
Exemplo
Aqui está um exemplo da execução de 9 threads em todos os cinco níveis de prioridade da enumeração Thread.Priority em que 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, nesse contexto, demonstra que a ordem de inicialização e priorização de thread pode nem sempre ser refletida na ordem de início de execuções de código nem de processo subsequentes. Ou seja, vemos aqui a natureza paralela da execução de código e a demonstração de fatias de tempo de processador atribuídas pelo sistema operacional para cada thread. Isso realça a influência e o controle do ambiente no qual os threads estão em execução. Dito isso, certamente vemos que os threads de prioridade mais alta estão de fato recebendo 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.
}