Шаг 237.
Язык программирования C#. Начала.
Многопоточное программирование. Класс Thread и создание потоков (окончание)

    На этом шаге мы рассмотрим небольшой пример создания и использования потока.

    Простой пример, в котором в программе создается дочерний поток, представлен в примере ниже.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.Threading;

namespace pr237_1
{
    // Класс с главным методом:
    class Program
    {
        // Статический метод (для вызова в дочернем потоке): 
        static void run() {
            Console.WriteLine("3anycкаем дочерний поток");
            // Конструкция цикла: 
            for(int k = 0; k <= 5; k++){
                // Отображение сообщения:
                Console.WriteLine("Дoчepний поток: {0}", (char)('A' + k));
                // Задержка выполнения потока в 1 секунду:
                Thread.Sleep(1000);
            }
            Console.WriteLine("Дoчepний поток завершен");
        }

        // Главный метод: 
        static void Main()
        {
            Console.WriteLine("3anycкаем главный поток");
            // Создание объекта дочернего потока:
            Thread mt = new Thread(run);
            // Запуск дочернего потока: 
            mt.Start();
            // Конструкция цикла: 
            for(int k = 1; k <= 5; k++){
                // Отображение сообщения:
                Console.WriteLine("Главный поток: " + k);
                // Задержка выполнения потока в 2 секунды: 
                Thread.Sleep(2000);
            }
            Console.WriteLine("Главный поток завершен");
            // Задержка:
            Console.ReadLine();
        }
    }
}
Архив проекта можно взять здесь.

    Результат выполнения программы может быть таким:


Рис.1. Результат работы приложения

    Программа достаточно простая. В главном классе описывается статический метод run(), который не возвращает результат и у которого нет аргументов (то есть этот метод соответствует библиотечному делегату ThreadStart). Мы собираемся выполнять код метода run() в режиме дочернего потока. В теле метода командой

  Console.WriteLine("3anycкаем дочерний поток");
отображается сообщение о начале выполнения дочернего потока, после чего запускается цикл, в котором индексная переменная k пробегает значения от 0 до 5 включительно, увеличивая каждый раз свое значение на единицу. За каждую итерацию цикла командой
  Console.WriteLine("Дoчepний поток: {0}", (char)('A' + k));
в консольном окне отображается сообщение, которое содержит очередную букву латинского алфавита, начиная с буквы 'A'. После этого командой
  Thread.Sleep(1000);
выполняется задержка в выполнении потока длительностью в 1 секунду. Здесь мы вызываем статический метод Sleep() из класса Thread. Действие метода состоит в том, что выполняется временная остановка в выполнении потока. Время приостановки потока определяется аргументом метода Sleep() и измеряется в миллисекундах (1 секунда содержит 1000 миллисекунд).


Таким образом, при выполнении метода run() в консольном окне отображаются сообщения, но отображаются они с интервалом в 1 секунду.

    После завершения конструкции цикла командой

  Console.WriteLine("Дoчepний поток завершен");
отображается сообщение о завершении выполнения потока.


Для использования класса Thread в программе, кроме пространства имен System, подключается еще и пространство имен System.Threading.

    Выполнение программы начинается с выполнения главного метода. В главном методе программы командой

  Console.WriteLine("3anycкаем главный поток");
отображается сообщение о начале выполнения программы (главного потока). Затем с помощью команды
  Thread mt = new Thread(run);
создается объект mt класса Thread. Это объект для дочернего потока. Аргументом конструктору передается имя метода run(). Следовательно, при выполнении потока, связанного с объектом mt, будет выполняться код метода run().


Конструктор класса Thread рассчитан на передачу в качестве аргумента ссылки на экземпляр делегата ThreadStart. Под эту ссылку выделяется соответствующая переменная (переменная типа делегата ThreadStart). По факту аргументом передается ссылка на метод. В результате переменной типа делегата присваивается ссылка на метод. Ситуация обрабатывается следующим образом: создается экземпляр делегата, ссылающийся на данный метод, а ссылка на экземпляр делегата записывается в переменную делегата. Поэтому вместо создания экземпляра делегата и передачи ссылки на этот экземпляр конструктору класса Thread мы можем сразу передать аргументом конструктору ссылку на метод.

    Но сам по себе факт создания объекта потока не означает, что поток начинает выполняться. Поток следует запустить на выполнение. Для этого из объекта потока вызывается метод Start() (команда

  mt.Start();
). В этот момент запускается дочерний поток: начинает выполняться метод run(). Но поскольку он выполняется в дочернем потоке, то следующая команда в главном потоке начинает выполняться, не дожидаясь окончания выполнения метода run(). В главном потоке выполняется конструкция цикла. На каждой итерации цикла командой
  Console.WriteLine("Главный поток: " + k);
отображается сообщение со значением индексной переменной k. Затем командой
  Thread.Sleep(2000);
выполняется задержка в 2 секунды в выполнении главного потока. По завершении конструкции цикла в главном потоке командой
  Console.WriteLine("Главный поток завершен");
в консольном окне отображается сообщение.


Получается, что главный и дочерний потоки одновременно отображают сообщения в консольном окне. Но для дочернего потока интервал между сообщениями составляет 1 секунду, а главный поток выводит сообщения с интервалом в 2 секунды. Понятно, что дочерний поток завершает работу раньше главного потока.

    На следующем шаге мы рассмотрим использование потоков.




Предыдущий шаг Содержание Следующий шаг