C# 4.0 полное руководство - 2011 - Герберт Шилдт
Шрифт:
Интервал:
Закладка:
Передача аргумента потоку
Первоначально в среде .NET Framework нельзя было передавать аргумент потоку, когда он начинался, поскольку у метода, служившего в качестве точки входа в поток, не могло быть параметров. Если же потоку требовалось передать какую-то информацию, то к этой цели приходилось идти различными обходными путями, например использовать общую переменную. Но этот недостаток был впоследствии устранен, и теперь аргумент может быть передан потоку. Для этого придется воспользоваться другими формами метода Start (), конструктора класса Thread, а также метода, служащего в качестве точки входа в поток.
Аргумент передается потоку в следующей форме метода Start ().
public void Start(object параметр)
Объект, указываемый в качестве аргумента параметр, автоматически передается методу, выполняющему роль точки входа в поток. Следовательно, для того чтобы передать аргумент потоку, достаточно передать его методу Start ().
Для применения параметризированной формы метода Start () потребуется следующая форма конструктора класса Thread:
public Thread(ParameterizedThreadStart запуск)
где запуск обозначает метод, вызываемый с целью начать выполнение потока. Обратите внимание на то, что в этой форме конструктора запуск имеет тип
ParameterizedThreadStart, а не ThreadStart, как в форме, использовавшейся в предыдущих примерах. В данном случае Parameter izedThreadS tart является делегатом, объявляемым следующим образом.
public delegate void ParameterizedThreadStart(object obj)
Как видите, этот делегат принимает аргумент типа obj ect. Поэтому для правильного применения данной формы конструктора класса Thread у метода, служащего в качестве точки входа в поток, должен быть параметр типа obj ect.
В приведенном ниже примере программы демонстрируется передача аргумента потоку.
// Пример передачи аргумента методу потока.
using System;
using System.Threading;
class MyThread { public int Count; public Thread Thrd;
// Обратите внимание на то, что конструктору класса // MyThread передается также значение типа int. public MyThread(string name, int num) {
Count = 0;
// Вызвать конструктор типа ParameterizedThreadStart // явным образом только ради наглядности примера.
Thrd = new Thread(this.Run);
Thrd.Name = name;
// Здесь переменная num передается методу Start ()
// в качестве аргумента.
Thrd.Start(num);
}
// Обратите внимание на то, что в этой форме метода Run()
// указывается параметр типа object.
’ void Run(object num) {
Console.WriteLine(Thrd.Name + " начат со счета " + num);
do {
Thread.Sleep (500);
Console.WriteLine("В потоке " + Thrd.Name + ", Count = " + Count);
Count++;
} while(Count < (int) num);
Console.WriteLine(Thrd.Name + " завершен.");
}
}
class PassArgDemo { static void Main() {
// Обратите внимание на то, что число повторений // передается этим двум объектам типа MyThread. MyThread mt = new MyThread("Потомок #1", 5); MyThread mt2 = new MyThread("Потомок #2", 3);
do {
Thread.Sleep(100);
} while (mt.Thrd.IsAlive'| mt2.Thrd.IsAlive);
Console.WriteLine("Основной поток завершен.");
}
}
Ниже приведен результат выполнения данной программы, хотя у вас он может оказаться несколько иным.
Потомок #1 завершен.
Основной поток завершен.
Как следует из приведенного выше результата, первый поток повторяется пять раз, а второй — три раза. Число повторений указывается в конструкторе класса MyThread и затем передается методу Run (), служащему в качестве точки входа в поток, с помощью параметризированной формы ParameterizedThreadStart метода Start ().
Свойство IsBackground
Как упоминалось выше, в среде .NET Framework определены две разновидности потоков: приоритетный и фоновый. Единственное отличие между ними заключается в том, что процесс не завершится до тех пор, пока не окончится приоритетный поток, тогда как фоновые потоки завершаются автоматически по окончании всех приоритетных потоков. По умолчанию создаваемый поток становится приоритетным. Но его можно сделать фоновым, используя свойство IsBackground, определенное в классе Thread, следующим образом.
public bool IsBackground { get; set; }
Для того чтобы сделать поток фоновым, достаточно присвоить логическое значение true свойству IsBackground. А логическое значение false указывает на то, что поток является приоритетным.
Приоритеты потоков
У каждого потока имеется свой приоритет, который отчасти определяет, насколько часто поток получает доступ к ЦП. Вообще говоря, низкоприоритетные потоки получают доступ к ЦП реже, чем высокоприоритетные. Таким образом, в течение заданного промежутка времени низкоприоритетному потоку будет доступно меньше времени ЦП, чем высокоприоритетному. Как и следовало ожидать, время ЦП, получаемое потоком, оказывает определяющее влияние на характер его выполнения и взаимодействия с другими потоками, исполняемыми в настоящий момент в системе.
Следует иметь в виду, что, помимо приоритета, на частоту доступа потока к ЦП оказывают влияние и другие факторы. Так, если высокоприоритетный поток ожидает доступа к некоторому ресурсу, например для ввода с клавиатуры, он блокируется, а вместо него выполняется низкоприоритетный поток. В подобной ситуации низкоприоритетный поток может получать доступ к ЦП чаще, чем высокоприоритетный поток в течение определенного периода времени. И наконец, конкретное планирование задач на уровне операционной системы также оказывает влияние на время ЦП, выделяемое для потока.
Когда порожденный поток начинает выполняться, он получает приоритет, устанавливаемый по умолчанию. Приоритет потока можно изменить с помощью свойства 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;