Книга: C# 4.0: полное руководство

Приоритеты потоков

Приоритеты потоков

У каждого потока имеется свой приоритет, который отчасти определяет, насколько часто поток получает доступ к ЦП. Вообще говоря, низкоприоритетные потоки получают доступ к ЦП реже, чем высокоприоритетные. Таким образом, в течение заданного промежутка времени низкоприоритетному потоку будет доступно меньше времени ЦП, чем высокоприоритетному. Как и следовало ожидать, время ЦП, получаемое потоком, оказывает определяющее влияние на характер его выполнения и взаимодействия с другими потоками, исполняемыми в настоящий момент в системе.

Следует иметь в виду, что, помимо приоритета, на частоту доступа потока к ЦП оказывают влияние и другие факторы. Так, если высокоприоритетный поток ожидает доступа к некоторому ресурсу, например для ввода с клавиатуры, он блокируется, а вместо него выполняется низкоприоритетный поток. В подобной ситуации низкоприоритетный поток может получать доступ к ЦП чаще, чем высокоприоритетный поток в течение определенного периода времени. И наконец, конкретное планирование задач на уровне операционной системы также оказывает влияние на время ЦП, выделяемое для потока.

Когда порожденный поток начинает выполняться, он получает приоритет, устанавливаемый по умолчанию. Приоритет потока можно изменить с помощью свойства Priority, являющегося членом класса Thread. Ниже приведена общая форма данного свойства:

public ThreadPriority Priority{ get; set; }

где ThreadPriority обозначает перечисление, в котором определяются приведенные ниже значения приоритетов.

ThreadPriority.Highest
ThreadPriority.AboveNormal
ThreadPriority.Normal
ThreadPriority.BelowNormal
ThreadPriority.Lowest

По умолчанию для потока устанавливается значение приоритета ThreadPriority.Normal.

Для того чтобы стало понятнее влияние приоритетов на исполнение потоков, обратимся к примеру, в котором выполняются два потока: один с более высоким приоритетом. Оба потока создаются в качестве экземпляров объектов класса MyThread. В методе Run() организуется цикл, в котором подсчитывается определенное число повторений. Цикл завершается, когда подсчет достигает величины 1000000000 или когда статическая переменная stop получает логическое значение true. Первоначально переменная stop получает логическое значение false. В первом потоке, где производится подсчет до 1000000000, устанавливается логическое значение true переменной stop. В силу этого второй поток оканчивается на следующем своем интервале времени. На каждом шаге цикла строка в переменной currentName проверяется на наличие имени исполняемого потока. Если имена потоков не совпадают, это означает, что произошло переключение исполняемых задач. Всякий раз, когда происходит переключение задач, имя нового потока отображается и присваивается переменной currentName. Это дает возможность отследить частоту доступа потока к ЦП. По окончании обоих потоков отображается число повторений цикла в каждом из них.

// Продемонстрировать влияние приоритетов потоков.
using System;
using System.Threading;
class MyThread {
  public int Count;
  public Thread Thrd;
  static bool stop = false;
  static string currentName;
  /* Сконструировать новый поток. Обратите внимание на то, что данный конструктор еще не начинает выполнение потоков. */
  public MyThread(string name) {
    Count = 0;
    Thrd = new Thread(this.Run);
    Thrd.Name = name; currentName = name;
  }
  // Начать выполнение нового потока,
  void Run() {
    Console.WriteLine("Поток " + Thrd.Name + " начат.");
    do {
      Count++;
      if(currentName != Thrd.Name) {
        currentName = Thrd.Name;
        Console.WriteLine("В потоке " + currentName);
      }
    } while(stop == false && Count < 1000000000);
    stop = true;
    Console.WriteLine("Поток " + Thrd.Name + " завершен.");
  }
}
class PriorityDemo {
  static void Main() {
    MyThread mt1 = new MyThread("с высоким приоритетом");
    MyThread mt2 = new MyThread("с низким приоритетом");
    // Установить приоритеты для потоков.
    mt1.Thrd.Priority = ThreadPriority.AboveNormal;
    mt2.Thrd.Priority = ThreadPriority.BelowNormal;
    // Начать потоки,
    mt1.Thrd.Start();
    mt2.Thrd.Start() ;
    mt1.Thrd.Join();
    mt2.Thrd.Join();
    Console.WriteLine();
    Console.WriteLine("Поток " + mt1.Thrd.Name +
           " досчитал до " + mt1.Count);
    Console.WriteLine("Поток " + mt2.Thrd.Name +
           " досчитал до " + mt2.Count);
  }
}

Вот к какому результату может привести выполнение этой программы.

Поток с высоким приоритетом начат.
В потоке с высоким приоритетом
Поток с низким приоритетом начат.
В потоке с низким приоритетом
В потоке с высоким приоритетом
В потоке с низким приоритетом
В потоке с высоким приоритетом
В потоке с низким приоритетом
В потоке с высоким приоритетом
В потоке с низким приоритетом
В потоке с высоким приоритетом
В потоке с низким приоритетом
В потоке с высоким приоритетом
Поток с высоким приоритетом завершен.
Поток с низким приоритетом завершен.
Поток с высоким приоритетом досчитал до 1000000000
Поток с низким приоритетом досчитал до 23996334

Судя по результату, высокоприоритетный поток получил около 98% всего времени, которое было выделено для выполнения этой программы. Разумеется, конкретный результат может отличаться в зависимости от быстродействия ЦП и числа других задач, решаемых в системе, а также от используемой версии Windows.

Многопоточный код может вести себя по-разному в различных средах, поэтому никогда не следует полагаться на результаты его выполнения только в одной среде. Так, было бы ошибкой полагать, что низкоприоритетный поток из приведенного выше примера будет всегда выполняться лишь в течение небольшого периода времени до тех пор, пока не завершится высокоприоритетный поток. В другой среде высокоприоритетный поток может, например, завершиться еще до того, как низкоприоритетный поток выполнится хотя бы один раз.

Оглавление книги


Генерация: 0.083. Запросов К БД/Cache: 0 / 0
поделиться
Вверх Вниз