Планирование потоков
Каждому потоку назначается определенный приоритет. Потокам, созданным в общеязыковой среде выполнения, изначально назначается приоритет ThreadPriority.Normal. Потоки, созданные за пределами среды выполнения, сохраняют приоритет, который они имели до входа в управляемую среду. Вы можете получить или задать приоритет для любого потока, используя свойство Thread.Priority.
Потоки назначаются на выполнение с учетом их приоритетов. Несмотря на то, что потоки выполняются в среде выполнения, процессорное время им выделяет операционная система. Сведения о алгоритме планирования, используемом для определения порядка выполнения потоков, зависят от каждой операционной системы. В некоторых операционных системах первым всегда выполняется поток с наивысшим приоритетом (из числа тех потоков, которые готовы к выполнению). Если доступны несколько потоков с одинаковым приоритетом, планировщик поочередно запускает все потоки с этим приоритетом, выделяя каждому фиксированное время для выполнения. Пока есть хоть один доступный для выполнения поток с более высоким приоритетом, ни один из потоков с более низким приоритетом не запускается. Когда не останется ни одного готового потока с этим приоритетом, планировщик назначает для выполнения потоки со следующим, более низким, приоритетом. Если в любой момент будет готов поток с более высоким приоритетом, поток с низким приоритетом сразу же прерывается и вместо него снова запускается поток с более высоким приоритетом. Кроме того, операционная система может динамически изменять приоритеты потоков при перемещении пользовательского интерфейса приложения на передний план или в фоновый режим. Другие операционные системы могут использовать другой алгоритм планирования.
Пример
Ниже приведен пример выполнения 9 потоков на всех 5 уровнях приоритета из Thread.Priority перечисления, где последние 5 находятся на самом высоком уровне приоритета. Кроме того, у нас есть поддержка обратного вызова из предыдущей статьи, что в этом контексте демонстрируется, что порядок инициализации и приоритета потоков не всегда может отражаться в последующем коде и не выполняется порядок запуска процессов. Это означает, что здесь мы видим параллельную природу выполнения кода и демонстрацию назначенных срезов времени процессора операционной системой для каждого потока. Это подчеркивает влияние и управление средой, в которой выполняются потоки. С этим мы, безусловно, видим, что наиболее приоритетные потоки действительно получают приоритет в выполнении.
Следующий код создает произвольные результаты для каждого выполнения. Однако распространенные шаблоны последовательности запускаемых приоритетов можно наблюдать после выполнения кода несколько раз и анализа выходных данных.
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.
}