Шрифт:
Интервал:
Закладка:
Функция, ссылка на которую передана в делегат, будет вызываться и исполняться некоторым рабочим потоком из пула как только произойдет одно из двух событий:
• Событие _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
MyDelegate hello = new MyDelegate(MyHello);
которому передается ссылка на метод MyHello этого же класса Test. Именно этот метод и формирует префикс вида Tеst_XXX:, где вместо XXX будет подставляться порядковый номер текущего вызова метода MyCallBack.
3. Порядковый номер вызова данного метода
При каждом вызове метода MyCallBack счетчик count увеличивается на единицу.
4. Информация о причине вызова данного метода
Через параметр timedOut метод MyCallBack получает от системы информацию о причине его вызова. Если получено значение false, то этот метод был вызван благодаря тому, что кто-то установил событие myEvent в состояние signaled. Значение true будет получено в том случае, если метод был вызван по причине завершения срока ожидания.
Последний метод NewEvent класса Test как раз и может использоваться клиентами для перевода события _myEvent в состояние signaled.
Теперь вновь обратимся к коду метода Main.
После создания экземпляра test класса Test вызывается его метод NewEvent в результате чего из пула рабочих потоков извлекается новый поток, который и выполняет метод MyCallBack. Напомним, что после этого событие _myEvent автоматически переходит в начальное состояние.
Далее основной поток засыпает на 500 тс. В связи с тем, что интервал ожидания, заданный четвертым параметром в ThreadPool.RegisterWaitForSingleObject равен 100 mc, метод MyCallBack будет вызван несколько раз по причине завершения периода ожидания.
Далее во второй раз вызывается метод NewEvent, и MyCallBack вызывается по причине перехода события myEvent в состояние signaled.
И, наконец, основной поток засыпает еще на 1000 mс, в течении которых MyCallBack вызывается с интервалом 100 mс по причине завершения времени ожидания.
Через 1000 mс основной поток просыпается и выполнение всего приложения (включая все рабочие потоки) завершается.
Ниже приводится вывод на консоль, полученный после запуска данного приложения:
>>> МуАрр thread = 16 IsPoolThread = False
>>> Test constructor thread = 16 IsPoolThread = False
>>> MyCallback thread = 18 IsPoolThread = True
Test_1: Count = 1 timedOut = False
>>> MyCallback thread = 18 IsPoolThread = True
Test_2: Count = 2 timedOut = True
>>> MyCallback thread = 18 IsPoolThread = True
Test_3: Count = 3 timedOut = True
>>> MyCallback thread = 18 IsPoolThread = True
Test_4: Count = 4 timedOut = True
>>> MyCallback thread = 18 IsPoolThread = True
Test_5: Count = 5 timedOut = True
>>> MyCallback thread = 18 IsPoolThread = True
Test_6: Count = 6 timedOut = True
>>> MyCallback thread = 18 IsPoolThread = True
Test_7: Count = 7 timedOut = False
>>> MyCallback thread = 18 IsPoolThread = True
Test_8: Count = 8 timedOut = True
>>> MyCallback thread = 18 IsPoolThread = True
Test_9: Count = 9 timedOut = True
>>>MyCallback thread = 18 IsPoolThread = True
Test_10: Count = 10 timedOut = True
>>> MyCallback thread = 18 IsPoolThread = True
Test_11: Count = 11 timedOut = True
>>> MyCallback thread = 18 IsPoolThread = True
Test_12: Count = 12 timedOut = True
>>> MyCallback thread = 18 IsPoolThread = True
Test_13: Count = 13 timedOut = True
>>>MyCallback thread = 18 IsPoolThread = True
Test_14: Count = 14 timedOut = True
>>> MyCallback thread = 18 IsPoolThread = True
Test_15: Count = 15 timedOut = True
>>> MyCallback thread = 18 IsPoolThread = True
Test 16: Count = 16 timedOut = True
Возвращаемся к коду инициализации атрибута
Теперь можно более подробно обсудить код метода InitIfNecessary. Все тело этого метода включено в критическую секцию lock(this) {}. Здесь this является ссылкой на экземпляр текущего класса (SynchronizationAttribute), который и является собственно свойством синхронизации (как контекста, так и домена синхронизации). Таким образом, при входе текущего потока в данную критическую секцию никакой другой поток не может войти в эту секцию (и в любую другую типа lock (obj) {}, где obj является ссылкой на данное свойство синхронизации).
Далее проверяется условие _asyncWorkEvent == null. Это условие выполняется только тогда, когда текущее