Шаг 240.
Язык программирования C#. Начала.
Многопоточное программирование. Использование потоков (окончание)

    На этом шаге мы рассмотрим создение дечерних потоков на основе экземпляра делегата и лямбда-выражения.

    Следующая программа, представленная ниже, иллюстрирует способ создания дочерних потоков на основе экземпляра делегата ThreadStart (имеется в виду явное создание экземпляра делегата и его передача в качестве аргумента конструктору класса Thread), а также на основе лямбда-выражения (когда лямбда-выражение передается аргументом конструктору класса Thread).

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

using System.Threading;

namespace pr240_1
{
    // Класс с методом для запуска в потоке: 
    class MyClass {
        // Закрытое текстовое поле: 
        private string text;
        // Закрытое целочисленное поле: 
        private int time;
        // Конструктор:
        public MyClass(string txt, int t) { 
            text = txt; 
            time = t;
        }
        // Метод без аргументов: 
        public void show(){
            for(int k = 1; k <= 5; k++){
                // Отображение сообщения: 
                Console.WriteLine(text + "\t" + k);
                // Задержка в выполнении потока: 
                Thread.Sleep(time);
            }
        }
    }

    // Главный класс: 
    class Program
    {
        // Статический метод с двумя аргументами: 
        static void run(string txt, int time) { 
            for(int k = 1; k <= 5; k++) {
                // Отображение сообщения: 
                Console.WriteLine(txt + "\t" + k);
                // Задержка в выполнении потока: 
                Thread.Sleep(time);
            }
        }
        // Главный метод: 
        static void Main()
        {
            // Начальное сообщение в главном потоке: 
            Console.WriteLine("Главный поток запущен..."); 
            // Создание объектов класса MyClass:
            MyClass A = new MyClass("Объект A", 1000);
            MyClass B = new MyClass("Объект B", 1500);
            // Создание экземпляра делегата:
            ThreadStart ts = A.show;
            // Добавление метода в список вызовов
            // экземпляра делегата: 
            ts += B.show;
            // Создание объекта потока на основе экземпляра 
            // делегата:
            Thread first = new Thread(ts);
            // Создание объекта потока на основе 
            // лямбда-выражения:
            Thread second = new Thread(() =>
            {
                run("Метод Alpha", 1200); 
                run("Метод Bravo", 800);
            });
            // Запуск первого потока: 
            first.Start();
            // Запуск второго потока: 
            second.Start();
            // Ожидание окончания выполнения первого потока: 
            if(first.IsAlive) first.Join();
            // Ожидание окончания выполнения второго потока: 
            if(second.IsAlive) second.Join();
            // Последнее сообщение в главном потоке:
            Console.WriteLine("Главный поток завершен...");
            // Задержка:
            Console.ReadLine();
        }
    }
}
Архив проекта можно взять здесь.

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


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

    У нас есть класс MyClass с методом show(), который выводит в консольное окно сообщения. Текст сообщения и задержка между сообщениями определяются полями объекта, из которого вызывается метод. При этом у метода нет аргументов и метод не возвращает результат.

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

    В главном методе создаются объекты A и B класса MyClass. Командой

  ThreadStart ts = A.show;
мы создаем экземпляр ts делегата ThreadStart, который ссылается на метод show() объекта A. После этого командой
  ts += B.show;
в список вызовов экземпляра делегата добавляется еще и ссылка на метод show() объекта B.

    На основе экземпляра ts создается объект first для первого дочернего потока (команда

  Thread first = new Thread(ts);
). При запуске этого потока командой
  first.Start();
сначала вызывается метод show() из объекта A, а по завершении выполнения метода вызывается метод show() из объекта B.


Таким образом, в первом дочернем потоке сначала выполняется метод show() из объекта A, а затем выполняется метод show() из объекта B.

    Еще один дочерний поток создается на основе лямбда-выражения. При создании объекта second классу Thread аргументом передается такое лямбда-выражение:

  () =>
            {
                run("Метод Alpha", 1200); 
                run("Метод Bravo", 800);
            }

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

  second.Start();
сначала вызывается метод run() с аргументами "Метод Alpha" и 1200, а затем этот же метод run() вызывается с аргументами "Метод Bravo" и 800.


Второй дочерний поток выполняется следующим образом: сначала выполняется команда
  run("Метод Alpha", 1200);    , 
а после того как команда выполнена, выполняется команда
  run("Метод Bravo", 800);     .

    Перед отображением последнего сообщения в главном потоке ожидается выполнение первого и второго дочерних потоков.

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




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