Поделиться через


Планирование потоков

Каждому потоку назначается определенный приоритет. Потокам, созданным в общеязыковой среде выполнения, изначально назначается приоритет 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.
}

См. также