Вспомните, что делегат ThreadStart может указывать только на методы, которые возвращают void и не принимают аргументов. В некоторых случаях это подходит, но если нужно передать данные методу, выполняющемуся во вторичном потоке, тогда придется использовать тип делегата ParametrizedThreadStart. В целях иллюстрации создайте новый проект консольного приложения по имени AddWithThreads и импортируйте пространство имен System.Threading. С учетом того, что делегат ParametrizedThreadStart может указывать на любой метод, принимающий параметр типа System.Object, постройте специальный тип, который содержит числа, подлежащие сложению:
namespace AddWithThreads
{
class AddParams
{
public int a, b;
public AddParams(int numb1, int numb2)
{
a = numb1;
b = numb2;
}
}
}
Далее создайте в классе Program статический метод, который принимает параметр AddParams и выводит на консоль сумму двух чисел:
void 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 просто используется ParametrizedThreadStart:
using 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:
AutoResetEvent _waitHandle = new AutoResetEvent(false);
Console.WriteLine("***** Adding with Thread objects *****");
Console.WriteLine("ID of thread in Main(): {0}",
Thread.CurrentThread.ManagedThreadId);
AddParams ap = new AddParams(10, 10);
Thread t = new Thread(new ParameterizedThreadStart(Add));
t.Start(ap);
// Ожидать, пока не поступит уведомление!
_waitHandle.WaitOne();
Console.WriteLine("Other thread is done!");
Console.ReadLine();
...
Когда другой поток завершит свою работу, он вызовет метод Set() на том же самом экземпляре типа AutoResetEvent:
void 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);
// Сообщить другому потоку о том, что работа завершена.
_waitHandle.Set();
}
}
Потоки переднего плана и фоновые потоки