На этом шаге мы рассмотрим создение дечерних потоков на основе экземпляра делегата и лямбда-выражения.
Следующая программа, представленная ниже, иллюстрирует способ создания дочерних потоков на основе экземпляра делегата 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 += B.show;
На основе экземпляра ts создается объект first для первого дочернего потока (команда
Thread first = new Thread(ts);
first.Start();
Еще один дочерний поток создается на основе лямбда-выражения. При создании объекта second классу Thread аргументом передается такое лямбда-выражение:
() => { run("Метод Alpha", 1200); run("Метод Bravo", 800); }
Это лямбда-выражение определяет метод без аргументов, при вызове которого последовательно вызывается метод run() с разными аргументами. Поэтому при запуске второго дочернего потока на выполнение командой
second.Start();
run("Метод Alpha", 1200); ,
run("Метод Bravo", 800); .
Перед отображением последнего сообщения в главном потоке ожидается выполнение первого и второго дочерних потоков.
На следующем шаге мы рассмотрим фоновые потоки.