Интернет-журнал "Домашняя лаборатория", 2007 №6
Шрифт:
using System;
using System.Threading;
public class Test {
private AutoResetEvent _myEvent;
private int _count = 0;
public Test {
Console.WriteLine("»> Test constructor thread = " +
Thread.CurrentThread.GetHashCode +
" IsPoolThread = " +
Thread.CurrentThread.IsThreadPoolThread);
_myEvent = new AutoResetEvent(false);
WaitOrTimerCallback myCallBackDelegate =
new WaitOrTimerCallback(this.MyCallBack);
ThreadPool.RegisterWaitForSingleObject Х
_myEvent,
myCallBackDelegate,
null,
100,
false);
}
public int count {
get { return _count; }
set { _count = value;}
}
private delegate String MyDelegate ;
private void MyCallBack (Object state, bool timedOut) {
Console.WriteLine("»> MyCallback thread = " +
Thread.CurrentThread.GetHashCode +
" IsPoolThread = " +
Thread.CurrentThread.IsThreadPoolThread);
MyDelegate hello = new MyDelegate(MyHello);
count++;
Console.WriteLine(hello + " Count = " + count +
" timedOut = " + timedOut);
}
private String MyHello {
return "Test_" + count +": ";
}
public void NewEvent {
_myEvent.Set;
}
}
public class MyApp {
public static void Main {
Console.WriteLine("ЮЮ> MyApp thread = " +
Thread.CurrentThread.GetHashCode +
" IsPoolThread = " +
Thread.CurrentThread.IsThreadPoolThread);
Test test = new Test;
test.NewEvent;
Thread.Sleep(500);
test.NewEvent;
Thread.Sleep(1000);
}
}
Опишем
Метод Main выполняется в основном потоке приложения. Все приложение в целом завершается по завершении этого метода (после возвращения из вызова функции Thread.Sleep (1000)). Параллельно с выполнением основного потока несколько раз успевает выполниться метод MyCallBack класса Test. Заметим, что этот метод выполняется так называемыми рабочими потоками, извлекаемыми системой из пула рабочих потоков. Рабочий поток не может пережить основной поток и по завершении последнего завершается и текущий рабочий поток.
Данное консольное приложение после запуска прежде всего выводит на консоль информацию о потоке, выполняющем код функции Main. Это хеш потока и информация о том, является ли данный поток рабочим потоком. Очевидно, что в данном случае значение последнего параметра должно быть равно false так как текущий поток является основным потоком приложения.
Далее вызывается конструктор класса Test. Здесь также выводится информация о потоке — том потоке, который выполняет код конструктора. Этот тот же самый основной поток приложения.
Далее в конструкторе создается экземпляр myEvent события типа AutoResetEvent. События в .NET еще не обсуждались в данном курсе, но в данном случае используется событие специального типа, реализованное в системе. В связи с этим пока будет достаточно рассмотреть только это событие и только в контексте данного примера.
Для работы с пулом потоков мы регистрируем с помощью статического метода ThreadPool.RegisterWaitForSingleObject делегат myCallBackDelegate специального известного системе типа WaitOrTimerCallback. Делегат является некоторым
аналогом указателя на функцию (в данном случае на MуCаllBаск), которая должна иметь сигнатуру, заданную при объявлении делегата. В случае делегата типа WaitOrTimerCallback возвращаемое значение должно отсутствовать (void), первый аргумент должен иметь тип System.Object и может использоваться для передачи вызываемой функции произвольных данных, второй параметр должен иметь тип bool и система через него передает значение true, если данный вызов произошел в связи с истечением времени ожидания (см. объяснение ниже).Функция, ссылка на которую передана в делегат, будет вызываться и исполняться некоторым рабочим потоком из пула как только произойдет одно из двух событий:
• Событие _myEvent (зарегестрированное вместе с делегатом) будет установлено в состояние signaled
Для установки события типа AutoResetEvent в данное состояние достаточно вызвать его метод Set .
• Время ожидания превысило пороговое значение (четвертый параметр в ThreadPool.RegisterWaitForSingleObject)
Отсчет времени идет от момента регистрации делегата или от момента последнего его вызова.
Заметим, что при создании myEvent вызывался конструктор AutoResetEvent (false). Задание параметра false привело к созданию события, не находящегося в состоянии signaled. При задании в этом конструкторе параметра true инициированное событие сразу же находится в состоянии signaled, и делегат вызывается сразу же после его регистрации.
Событие AutoResetEvent обладает еще одним важным свойством, отраженным в его названии — оно переходит в исходное состояние автоматически после очередного вызова делегата.
Третий параметр в ThreadPool.RegisterwaitForSingleObject задает ссылку на объект, содержащий данные передаваемые связанной с делегатом функции при вызове последнего (в данном случае ничего не передается).
Последний параметр определяет, что делегат зарегистрирован навсегда (точнее в данном случае до момента уничтожения экземпляра класса Test, в конструкторе которого выполняется регистрация). Если бы последний параметр равнялся true, регистрация делегата была бы действительна только на один вызов.
Метод MyCallBack, ссылка на который передана конструктору при создании делегата myCallBackDelegate, прежде всего выводит на консоль информацию о потоке, в котором он исполняется. Как и раньше это хеш потока и данные о его типе. В данном случае мы полагаем, что значение последнего параметра при каждом вызове этого метода будет равно true, так как выполняться этот метод должен рабочим потоком.
Далее MyCallBack выводит на консоль некоторое сообщение. Это сообщение состоит из следующих частей:
1. Префикс
Ради демонстрации того, как можно объявлять делегат нового типа, префикс формируется излишне сложно — посредством использования делегата нового типа — MyDelegate. Из его объявления
2. private delegate String MyDelegate ;
видно, что с делегатом данного типа можно связать любую функцию, возвращающую String и не имеющую аргументов. В теле метода MyCallBack создается новый делегат hello