Чтение онлайн

ЖАНРЫ

ЯЗЫК ПРОГРАММИРОВАНИЯ С# 2005 И ПЛАТФОРМА .NET 2.0. 3-е издание

Троелсен Эндрю

Шрифт:

 Console.WriteLine("Имя текущего домена приложения: {0}";

 Thread.GetDomain.FriendlyName);

 Console.WriteLine("Идентификатор текущего контекста: {0}", Thread.CurrentContext.ContextID);

 // Вывод информации о данном потоке.

 Console.WriteLine("Имя потока: {0}", primaryThreаd.Name);

 Console.WriteLine("Запущен ли поток? {0}", primaryThread.IsAlive);

 Console.WriteLine("Уровень приоритета: {0}", primaryThread.Priority);

 Console.WriteLine("Состояние потока: {0}", primaryThread.ThreadState);

 Console.ReadLine;

}

На

рис. 14.5 показан вывод этого приложения.

Рис. 14.5. Сбор статистики о потоке

Свойство Name

Приведенный выше программный код достаточно понятен, но обратите внимание на то, что класс Thread предлагает свойство с именем Name (имя). Если вы не установите для него значения, свойство Name будет возвращать пустую строку. Но, назначив данному объекту Thread в качестве имени понятную строку, вы можете сильно упростить процесс отладки. В Visual Studio 2005 в режиме отладки можно использовать окно Threads (Потоки), доступ к которому можно получить, выбрав Debug->Windows->Threads из меню. Как показано на рис. 14.6, в этом окне можно по имени идентифицировать поток, который следует проанализировать.

Рис. 14.6. Отладка потока в Visual Studio 2005

Свойство Priority

Далее заметим, что тип Thread определяет свойство с именем Priority. По умолчанию все потоки получают приоритет Normal (средний). Но вы можете изменить это значение в любой момент времени существования потока, используя свойство Priority и связанный с ним перечень System.Threading.ThreadPriority.

public enum ThreadPriority {

 AboveNormal,

 BelowNormal,

 Highest,

 Idle,

 Lowest,

 Normal, // Значение, используемое по умолчанию.

 TimeCritical

}

При назначении потоку приоритета, отличного от принимаемого по умолчанию (ThreadPriority.Normal), вы должны понимать, что не обладаете слишком большими возможностями контроля в отношении того, когда планировщик потоков переключится с одного потока на другой. Уровень приоритета потока является лишь "подсказкой" среде CLR в отношении того, насколько важно выполнение данного потока. Поэтому поток со значением ThreadPriority.Highest (наивысший) не обязательно гарантирует данному потоку абсолютное преимущество.

Снова подчеркнем, что в том случае, когда планировщик потоков полностью занят текущей задачей (например, синхронизацией объекта, переключением или перемещением потоков), уровень приоритета будет, вероятнее всего, соответствующим образом изменен. Однако в других случаях соответствующие значения прочитает среда CLR, которая и выдаст планировщику потоков указания о том, как лучше всего организовать квантование времени. При прочих равных условиях потоки с идентичным приоритетом должны получать примерно одинаковое время для выполнения своей работы.

Необходимость изменения приоритетов потоков вручную возникает очень редко. Теоретически можно повысить приоритет для множества потоков так, что это не позволит потокам с более низкими приоритетами выполнять работу на их уровнях (поэтому используйте указанные возможности с осторожностью).

Исходный код. Проект ThreadState размещен в подкаталоге, соответствующем главе 14.

Программное

создание вторичных потоков

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

1. Для выбранного типа создайте метод, который будет использоваться в качестве точки входа нового потока.

2. Создайте делегат ParameterizedThreadStart (или уже устаревший ThreadStart), передав его конструктору адрес метода, определенного на шаге 1.

3. Создайте объект Thread, передав конструктору делегат ParameterizedThreadStart/ThreadStart в виде аргумента.

4. Задайте подходящие начальные характеристики потока (имя, приоритет и т.д.).

5. Вызовите метод Thread.Start. Это указание как можно быстрее стартовать поток для метода, на который ссылается делегат, созданный на шаге 2.

Согласно шагу 2, имеется возможность использовать один из двух разных типов делегата для метода, предназначенного для выполнения во вторичном потоке. Делегат ThreadStart является частью пространства имен System.Threading со времен .NET версии 1.0 и может указывать на любой метод, не имеющий аргументов и не возвращающий ничего. Этот делегат удобно использовать тогда, когда метод должен выполняться в фоновом режиме без взаимодействия с ним.

Очевидным ограничением ThreadStart является отсутствие параметров. Поэтому в .NET 2.0 предлагается тип делегата ParameterizedThreadStart, допускающий передачу одного параметра типа System.Object. Поскольку с помощью System.Object можно представить всё, что угодно, вы можете передать этому делегату любое число параметров в виде пользовательского класса или структуры. Заметьте, однако, что делегат ParameterizedThreadStart может указывать только на методы, возвращающие void.

Работа с делегатом ThreadStart

Чтобы рассмотреть процесс создания многопоточного приложения на практику (а также продемонстрировать пользу соответствующего подхода), предположим, что у нас есть консольное приложение (SimpleMultiThreadApp), которое позволяет конечному пользователю выбрать в приложении либо использование одного первичного потока, выполняющего всю работу, либо разделение ее на два отдельных потока.

После того как вы обеспечите доступ к пространству имен System.Threading с помощью ключевого слова C# using, первым шагом должно быть определение метода, который будет выполнять работу во вторичном потоке. Чтобы сосредоточиться на сути механизма построения многопоточных программ, здесь этот метод просто выводит последовательность чисел с двухсекундными задержками перед каждой операцией вывода. Вот полное определение соответствующего класса Printer.

public class Printer {

 public void PrintNumbers {

// Отображение информации потока.

Console.WriteLine ("-› {0} выполняет PrintNumbers", Thread.CurrentThread.Name);

// Вывод чисел.

Console.Write("Ваши числа: ");

for(int i = 0; i ‹ 10; i++) {

Console.Write(i + ", ");

Thread.Sleep(2000);

}

Console.WriteLine;

 }

}

Теперь в Main нужно предложить выбор одного или двух потоков для выполнения задач приложения. Если пользователь выберет использование одного потока, просто вызывается метод PrintNumbers в рамках первичного потока. Но если пользователь указывает два потока, создается делегат ThreadStart, указывающий на PrintNumbers. Объект делегата передается конструктору нового объекта Thread и вызывается метод Start, информирующий среду CLR о том, что поток готов к обработке.

Поделиться с друзьями: