Plánování vláken
Každé vlákno má přiřazenou prioritu vlákna. Vlákna vytvořená v modulu CLR (Common Language Runtime) mají zpočátku přiřazenou prioritu ThreadPriority.Normal. Vlákna vytvořená mimo modul runtime si zachovají prioritu, kterou měli před vstupem do spravovaného prostředí. Můžete získat nebo nastavit prioritu libovolného vlákna s Thread.Priority vlastností.
Vlákna jsou naplánovaná na spuštění na základě jejich priority. I když se vlákna spouští v rámci modulu runtime, operační systém přiřazuje všechna vlákna časových řezů procesoru. Podrobnosti o algoritmu plánování použitém k určení pořadí, ve kterém se vlákna spouští, se liší podle jednotlivých operačních systémů. V některých operačních systémech je vlákno s nejvyšší prioritou (z vláken, která lze spustit), vždy naplánováno tak, aby se spustilo jako první. Pokud je k dispozici více vláken se stejnou prioritou, plánovač prochází vlákny s danou prioritou, čímž každému vláknu poskytne pevný časový řez, ve kterém se má provést. Dokud je vlákno s vyšší prioritou k dispozici ke spuštění, vlákna s nižší prioritou se nespustí. Pokud v dané prioritě nejsou spuštěná další vlákna, plánovač se přesune na další nižší prioritu a naplánuje vlákna s danou prioritou pro spuštění. Pokud se vlákno s vyšší prioritou spustí, vlákno s nižší prioritou se předpne a vlákno s vyšší prioritou se může spustit znovu. Kromě toho může operační systém také dynamicky upravovat priority vláken, protože uživatelské rozhraní aplikace se přesouvá mezi popředím a pozadím. Jiné operační systémy se můžou rozhodnout použít jiný algoritmus plánování.
Příklad
Tady je příklad spuštění 9 vláken ve všech 5 úrovních priority z Thread.Priority výčtu, kde posledních 5 je na nejvyšší úrovni priority. Máme také podporu zpětného volání z předchozího článku, která v tomto kontextu ukazuje, že pořadí inicializace vlákna a stanovení priorit nemusí být vždy promítnuto v následném kódu ani zpracování počátečního pořadí provádění. To znamená, že zde vidíme paralelní povahu provádění kódu a ukázku přiřazených časových řezů procesoru operačním systémem pro každé vlákno. Tím se zvýrazní vliv a řízení prostředí, ve kterém běží vlákna. Díky tomu jistě vidíme, že vlákna s nejvyšší prioritou skutečně dostávají prioritu při provádění.
Následující kód při každém spuštění vytvoří libovolné výsledky. Běžné vzory sekvencí spuštěných priorit se ale dají pozorovat po vícenásobném spuštění kódu a analýze výstupů.
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.
}