Следующий вариант организации взаимодействия между потоками основан на использовании общедоступных свойств. От предыдущего примера отличается тем, что доступ к закрытому счётчику lVal в соответствии с принципами инкапсуляции осуществляется через свойство с блоками get (акцессор) и set (мутатор).
using System;
using System.Threading;
namespace CommunicatingThreadsData
{
public delegate void CallBackFromStartClass (long param);
// Данные. Предмет и основа взаимодействия двух потоков.
class CommonData
{
private long lVal;
public long lValProp
{
get
{
return lVal;
}
set
{
lVal = value;
}
}
public CommonData(long key)
{
lVal = key;
}
}
// Классы Worker и Inspector: основа взаимодействующих потоков.
class Worker
{
CommonData cd;
// Конструктор умолчания...
public Worker(ref CommonData rCDKey)
{
cd = rCDKey;
}
public void startWorker()
{
DoIt(ref cd);
}
// Тело рабочей функции...
public void DoIt(ref CommonData cData)
{//====================================
for (;;)
{
cData.lValProp++;
Console.Write(“{0,25}\r”,cData.lValProp);
}
}//====================================
}
class Inspector
{
long stopVal;
CommonData cd;
CallBackFromStartClass callBack;
// Конструктор...
public Inspector(ref CommonData rCDKey, long key, CallBackFromStartClass cbKey)
{
stopVal = key;
cd = rCDKey;
callBack = cbKey;
}
public void startInspector()
{
measureIt(ref cd);
}
// Тело рабочей функции...
public void measureIt(ref CommonData cData)
{//====================================
for (;;)
{
if (cData.lValProp < stopVal)
{
Thread.Sleep(100);
Console.WriteLine(“\n{0,-25}”,cData.lValProp);
}
else
callBack(cData.lValProp);
}
}//====================================
}
class StartClass
{
static Thread th0, th1;
static CommonData cd;
static long result = 0;
static void Main(string[] args)
{
StartClass.cd = new CommonData(0);
// Конструкторы классов Worker и Inspector несут дополнительную нагрузку.
// Они обеспечивают необходимыми значениями методы,
// выполняемые во вторичных потоках.
Worker work;
// До начала выполнения потока вся необходимая информация доступна методу.
work = new Worker(ref cd);
Inspector insp;
// На инспектора возложена дополнительная обязанность вызова функции-терминатора.
// Для этого используется специально определяемый и настраиваемый делегат.
insp = new Inspector(ref cd, 50000, new CallBackFromStartClass(StartClass.StopMain));
// Стартовые функции потоков должны соответствовать сигнатуре
// класса делегата ThreadStart. Поэтому они не имеют параметров.
ThreadStart t0, t1;
t0 = new ThreadStart(work.startWorker);
t1 = new ThreadStart(insp.startInspector);
// Созданы вторичные потоки.
StartClass.th0 = new Thread(t0);
StartClass.th1 = new Thread(t1);
// Запущены вторичные потоки.
StartClass.th0.Start();
StartClass.th1.Start();
// Ещё раз о методе Join(): Выполнение главного потока приостановлено.
StartClass.th0.Join();
StartClass.th1.Join();
// Потому последнее слово остаётся за главным потоком приложения.
Console.WriteLine(“Main(): All stoped at {0}. Bye.», result);
}
// Функция-член класса StartClass выполняется во ВТОРИЧНОМ потоке!
public static void StopMain(long key)
{
Console.WriteLine(“StopMain: All stoped at {0}...”, key);
// Остановка рабочих потоков. Её выполняет функция-член
// класса StartClass. Этой функции в силу своего определения
// известно ВСЁ о вторичных потоках. Но выполняется она
// в ЧУЖОМ (вторичном) потоке. Поэтому:
// 1. надо предпринять особые дополнительные усилия для того чтобы
// результат работы потоков оказался доступен в главном потоке.
/*StartClass.*/result = key;
// 2. очень важна последовательность остановки потоков,
StartClass.th0.Abort();
StartClass.th1.Abort();
// Этот оператор не выполняется! Поток, в котором выполняется
// метод-член класса StartClass StopMain() остановлен.