Язык программирования C#9 и платформа .NET5
Console.WriteLine("***** The Amazing Thread App *****\n");Console.Write("Do you want [1] or [2] threads? ");string threadCount = Console.ReadLine(); // Запрос количества потоков// Назначить имя текущему потоку.Thread primaryThread = Thread.CurrentThread;primaryThread.Name = "Primary";// Вывести информацию о потоке.Console.WriteLine("-> {0} is executing Main()",Thread.CurrentThread.Name);// Создать рабочий класс.Printer p = new Printer();switch(threadCount){case "2":// Создать поток.Thread backgroundThread =new Thread(new ThreadStart(p.PrintNumbers));backgroundThread.Name = "Secondary";backgroundThread.Start();break;case "1":p.PrintNumbers();break;default:Console.WriteLine("I don't know what you want...you get 1 thread.");goto case "1"; // Переход к варианту с одним потоком}// Выполнить некоторую дополнительную работу.Console.WriteLine("This is on the main thread, and we are finished.");Console.ReadLine();Если теперь вы запустите программу с одним потоком, то обнаружите, что финальное окно сообщения не будет отображать сообщение, пока вся последовательность чисел не выведется на консоль. Поскольку после вывода каждого числа установлена пауза около 2 секунд, это создаст не особенно приятное впечатление у конечного пользователя. Однако в случае выбора двух потоков окно сообщения отображается немедленно, потому что для вывода чисел на консоль выделен отдельный объект
.ThreadРабота с делегатом ParametrizedThreadStart
Вспомните, что делегат
может указывать только на методы, которые возвращаютThreadStartи не принимают аргументов. В некоторых случаях это подходит, но если нужно передать данные методу, выполняющемуся во вторичном потоке, тогда придется использовать тип делегатаvoid. В целях иллюстрации создайте новый проект консольного приложения по имениParametrizedThreadStartи импортируйте пространство именAddWithThreads. С учетом того, что делегатSystem.Threadingможет указывать на любой метод, принимающий параметр типаParametrizedThreadStart, постройте специальный тип, который содержит числа, подлежащие сложению:System.Objectnamespace AddWithThreads{class AddParams{public int a, b;public AddParams(int numb1, int numb2){a = numb1;b = numb2;}}}Далее создайте в классе
статический метод, который принимает параметрProgramи выводит на консоль сумму двух чисел:AddParamsvoid Add(object data){if (data is AddParams ap){Console.WriteLine("ID of thread in Add(): {0}",Thread.CurrentThread.ManagedThreadId);Console.WriteLine("{0} + {1} is {2}",ap.a, ap.b, ap.a + ap.b);}}Код в файле
прямолинеен. Вместо типаProgram.csпросто используетсяThreadStart:ParametrizedThreadStartusing System;using System.Threading;using AddWithThreads;Console.WriteLine("***** Adding with Thread objects *****");Console.WriteLine("ID of thread in Main(): {0}",Thread.CurrentThread.ManagedThreadId);// Создать объект AddParams для передачи вторичному потоку.AddParams ap = new AddParams(10, 10);Thread t = new Thread(new ParameterizedThreadStart(Add));t.Start(ap);// Подождать, пока другой поток завершится.Thread.Sleep(5);Console.ReadLine();Класс AutoResetEvent
В приведенных выше начальных примерах нет какого-либо надежного способа узнать, когда вторичный поток завершит свою работу. В последнем примере метод
вызывался с произвольным временным периодом, чтобы дать возможность другому потоку завершиться. Простой и безопасный к потокам способ заставить один поток ожидать, пока не завершится другой поток, предусматривает применение классаSleep(). В потоке, который должен ожидать, создайте экземплярAutoResetEventи передайте его конструктору значениеAutoResetEvent, указав, что уведомления пока не было. Затем в точке, где требуется ожидать, вызовите методfalse. Ниже приведен модифицированный классWaitOne(), который делает все описанное с использованием статической переменной-членаProgram:AutoResetEvent