Console.WriteLine("Нажмите «Enter» для завершения работы…");

 Console.ReadLine();

}

В данном случае метод PrintTime() будет вызываться примерно каждую секунду и методу не передается никакой дополнительной информации. Чтобы передать целевому объекту делегата какую-то информацию, замените значение null второго параметра конструктора подходящим значением (например, "Привет"). Следующая модификация метода PrintTime() использует переданное значение.

static void PrintTime(Object state) {

 Console.WriteLine("Время: {0}, Параметр: {1}", DateTime.Now.ToLongTimeString(), state.ToString());

}

На рис. 14.11 показан соответствующий вывод.

Рис. 14.11. Таймеры за работой

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

<p>Пул потоков CLR</p>

Заключительной темой нашего обсуждения в этой плаве, посвященной потокам, будет пул потоков CLR. При асинхронном вызове типов с помощью делегатов (посредством метода BeginInvoke()) нельзя сказать, что среда CLR буквально создает совершенно новый поток. В целях эффективности метод BeginInvoke() делегата использует пул (динамическую область) рабочих потоков, поддерживаемых средой выполнения. Чтобы позволить вам взаимодействовать с этим пулом рабочих потоков, пространство имен System.Threading предлагает тип класса ThreadPool.

Чтобы поставить вызов метода в очередь для обработки рабочим потоком из пула, используйте метод ThreadPool.QueueUserWorkItem(). Этот метод является перегруженным, чтобы вдобавок к экземпляру делегата WaitCallback имелась возможность указать необязательный System.Objеct для пользовательских данных состояния.

public sealed class ThreadPool {

 …

 public static bool QueueUserWorkItem(WaitCallback callBack);

 public static bool QueueUserWorkItem(WaitCallback callBack, object state);

}

Делегат WaitCallback может указывать на любой метод, имеющий один параметр System.Object (для представления необязательных данных состояния) и не возвращающий ничего. Если при вызове QueueUserWorkItem() вы не предложите System.Object, среда CLR автоматически передаст значение null. Для иллюстрации методов очереди при использовании пула потоков CLR давайте рассмотрим следующую программу, в которой снова используется тип Printer. Но на этот раз мы не будем создавать массив типов Thread вручную, а свяжем метод PrintNumbers() с членами пула.

class Program {

 static void Main(string[] args) {

  Console.WriteLine("Старт главного потока. ThreadID = {0}", Thread.CurrentThread.GetHashCode());

  Printer p = new Printer();

  WaitCallback workItem = new WaitCallback(PrintTheNumbers);

  // Очередь из 10 вызовов метода.

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

   ThreadPool.QueueUserWorkItem(workItem, p);

  }

  Console.WriteLine("Все задачи в очереди");

  Console.ReadLine();

 }

 static void PrintTheNumbers(object state) {

  Printer task = (Printer)state;

  task.PrintNumbers();

 }

}

Здесь вы можете спросить, разве выгодно использовать поддерживаемый средой CLR пул потоков вместо явного создания объектов Thread? Тогда рассмотрите следующие главные преимущества использования пула.

• Пул потоков управляет потоками эффективнее, поскольку минимизируется число потоков, которые приходится создавать, запускать и останавливать.

• При использовании пула потоков вы можете сосредоточиться на своей конкретной задаче, не отвлекаясь на вопросы инфраструктуры потоков приложения.

Однако управление потоками "вручную" может оказаться предпочтительнее, например, в следующих случаях.

• Если требуется создавать приоритетные потоки или устанавливать приоритеты потоков. Потоки, помещенные в пул, всегда являются фоновыми потоками с обычным уровнем приоритета (ThreadPriority.Normal).

• Если требуется создать поток с фиксированным идентификатором, чтобы име-лаcь возможность завершить, приостановить или обнаружить его по имени.

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

Перейти на страницу:

Похожие книги