Поток — фактически выполняемая ветвь кода программы. Один или более потоков, запущенных внутри процесса, делают возможным сосуществование многочисленных выполняемых ветвей в рамках одного процесса. Например, используя потоки, программа может модифицировать интерфейс пользователя частичными результатами работы одного потока во время проведения вычислений другим потоком. Все потоки в одном и том же процессе совместно используют среду процесса так, чтобы все они могли обращаться к памяти процесса.
Операционная система планирует процессорное время, занимаемое потоками. Для процессов и прикладных областей (прикладные области обсуждаются в этой главе позже) планирование процессорного времени не выполняется. Потокам периодически дается ограниченный период процессорного времени. Это делается для того, чтобы они могли совместно использовать процессор. Потокам с более высоким приоритетом отрезки времени будут предоставляться более часто, чем потокам с более низким приоритетом. После того, как проходит некоторое время, выполняющийся поток предоставляет шанс запуска другому потоку. Когда потоку снова предоставляется процессорное время, он продолжает выполнение с той точки, в которой его работа была приостановлена.
Потоки поддерживают контекст, который сохраняется и восстанавливается, когда планировщик операционной системы переключает потоки. Контекст потока содержит регистры центрального процессора и стек, в которых хранится состояние выполняющегося кода.
Класс System: : Threading:: Thread (Система::Организация поточной обработ-ки::Поток) моделирует выполняющийся поток. Объект Thread (Поток), который представляет текущий выполняющийся поток, может быть найден с помощью статического свойства Thread :: CurrentThread.
Исключая случаи, когда код выполняется на многопроцессорной машине или когда какой-нибудь поток пробует занять время процессора, в то время как другой поток на однопроцессорной машине пребывает в состоянии ожидания некоторого события типа ввода-вывода, использование нескольких потоков не приводит к ускорению решения вычислительных задач. Благодаря потокам, однако, удается сократить время ответа сие--темы в тех задачах, где требуется взаимодействие с пользователем. При использовании слишком большого количества потоков производительность может уменьшиться. Это происходит из-за накладных расходов на управление потоками и из-за соперничества между конкурирующими потоками, которое также приводит к затратам вычислительных ресурсов центрального процессора.
Чтобы помочь читателю лучше разобраться в работе потоков, мы предоставим многошаговый пример Threading (Организация поточной обработки), в котором используются сборки Customer (Клиент) и Hotel (Гостиница). Сначала мы ознакомимся с программой, а затем выполним резервирование. Итак, для начала рассмотрим шаг 0.
Потоки .NET выполняются как делегаты, определенные классом ThreadStart. Делегат ничего не возвращает (void) и не имеет никаких параметров.
public _gc _delegate void ThreadStart();
// сборщик мусора - делегат ThreadStart ();
Класс NewReservation имеет общедоступный (public) метод MakeReservation, который определяет функцию потока. Так как функция потока не принимает никаких параметров, то в качестве данных она может использовать только поля экземпляра NewReservation.
Делегат потока создается и передается в качестве параметра для конструктора, который создает объект Thread (Поток). Метод Start (Пуск) объекта Thread (Поток) вызывается для того, чтобы начать выполнение потока. Когда мы будем рассматривать модель программирования асинхронных событий, мы покажем, как передать параметры делегату потока. Программа теперь имеет два потока: первоначальный, который выполнял код запуска потока, и поток, который мы только что создали и который попытается забронировать места в гостинице.
public _gc class NewReservation
// класс сборщика мусора NewReservation
{
public:
void MakeReservation()
{
Console::WriteLine(
"Thread {0} starting.", // Запускается поток {О}.
Thread::CurrentThread-> // Поток
GetHashCode().ToString());
ReservationResult *result =
hotelBroker->MakeReservation(
customerld, city, hotel, date, numberDays);
// customerld, город, гостиница, дата, numberDays
}
};
Затем в методе Main (Главный) следующий код запускает поток, используя при этом делегат:
NewReservation *reservel =
new NewReservation(customers, hotelBroker); // клиенты
reservel->customerld = 1;
reservel->city = "Boston"; // город = "Бостон";
reservel->hotel = "Presidential"; // гостиница = "Президентская";
reservel->sdate = "12/12/2001";
reservel->numberDays = 3;
// создать делегат для потоков
ThreadStart *threadStartl = new ThreadStart(
reservel,
reservel->MakeReservation);
Thread *threadl = new Thread(threadStartl); // новый Поток
Console::WriteLine(
"Thread {0} starting a new thread.",
// "Поток {0} запускает новый поток. "
Thread::CurrentThread-> // Поток
GetHashCode().ToString());
threadl->Start ();
Чтобы заставить первоначальный поток ожидать завершения работы второго потока, вызывается метод Join (Объединить) объекта Thread (Поток). Первоначальный поток теперь заблокирован (находится в состоянии ожидания), пока не завершит работу поток, резервирующий место в гостинице. Результат запроса резервирования места выводится на консоль резервирующим потоком.
// Блокировать этот поток, пока не завершится рабочий поток