Нэш Трей - C# 2010. Ускоренный курс для профессионалов (2010) (1160865), страница 109
Текст из файла (страница 109)
реализующего 1Аэупсреэп1С, как и не заинтересован в установке метода обратного вызова, который должен быть вызван при завершении работы. Этот вызов в стиле "сделал и забыл" — отважная попытка повысить эффективность сервера. Однако результат неудовлетворителен, поскольку здесь пропал оператор пз1пс из предыдущей версии сервера. Объект яос)теС не закрывается вовремя, и удаленные соединения остаются открытыми до тех пор, пока сборщик мусора не решит финализировать объекты Яосаеп.
Поэтому асинхронный вызов должен включать обратный вызов, чтобы закрыть соединение. Для прослушивающего потока было бы бессмысленно ожидать метода ЕпбБепб, поскольку это вернуло бы ту же неэффективность, которая существовала ранее. На заметку! Полученный из запуска асинхронной операции объект, реализующий 1Азупскевп1с, должен реализовывать свойство 1Азупсневп1с. Аэупснабснапб1е, чтобы позволить пользователям получать дескриптор, на котором потом можно организовать ожидание. В случае Яос)тес возвращается экземпляр Оуег1врреблзупскезп1С, Этот класс в конечном итоге наследуется от Буэгеш.
Мег. Ьвгулэупскеэп1С. Он в действительности не создает события для ожидания до тех пор, пока к нему не произойдет обращение через свойство 1Азупсееэп1с. Аэупсиабснапб1е. Такое "ленивое" создание избавляет от ненужного создания обьекга блокировки, который большую часть времени остается неиспользованным. К тому же на объект Оуег1арреблвупскеэп1С возлагается обязанность по окончании работы закрывать дескриптор операционной системы.
()(нсгопотонносгь з ь(( 409 Однако перед тем как перейти к обратному вызову, присмотримся к потоку слушателя. Все, что он делает — ожидает входящие запросы. Не будет ли более эффективным организовать прослушивание тоже через потоки из пула г Разумеется! Ниже приведен новый усовершенствованный сервер "Не!1о РУог]НГ'. который в полной мере использует пул потоков процесса: пятпд Яуягегп пятпд Яузгеж.техТ4 пяупч Яузгеи.ТЬгеабЬпТП вяупд Яуягет.кеТ4 вяупЧ Яуягеж.кег.Бес«егя; рпЬ11с с1аяя ЕпггуРо1пТ ( ргтчаге сопяг Ьпг СоппесТОпевеьепдТЬ = 4; ргтчаге сопзг 1пТ Ь1ягепрогТ = 1234; рг1чаге сопяТ ЬпТ МахсоппесТ1опНапо1егя = 4; ргучаге згаТ1с чо1<( Напо1есоппесТ1оп( 1АяупсНезп1Т аг ) ( Яос«ег 11ягепег = (Яос«ег) аг.АяупсЯТаге; Яос«ег пенСоппесТ1оп = 11ягепег.ЕпсАссерТ( аг ); ЬУТе(] тзо = ЕпсоСЬпч.ПТРЯ >ЯеТВУТез( "Не11о Иог1С!" ); пеиСоппесТЬоп.ВеЧгпБепо( тяч, О, изей.ЬепдТЬ, Яос«еТР1аЧя.копе, пен АяупсСа11Ьас«( ЕпггуротпТ.С1ояеСоппесТЬоп), пенСоппесгуоп ); // Поместить другой запрос в очередь.
11ягепег.ВечтпАссерТ( пен АяупсСа11Ьас«(ЕпТгуРотпТ.Нап01еСоппесТЬоп), 11ягепег ); ) яТаТ1с чо1с( С1ояеСоппесТ1оп( 1Аяупскеяп1Т аг ) ( Бос«ег Тнезос«ег = (Яос«ес) аг.АяупсБТаге; ТЬеЯос«еТ.С1ояе(): ) ягагус чо1ь) Ма].п () ( Бос«ег 11ягепзос« = пен Яос«ег( Ась(геязуав11у.1пгегкегног«, Яос«егтуре.зггеаи, Ргогосо1туре.тср )4 11яТепБос«.ВЬпс( пен ЬРЕпбро1пг(ЬРАНя(геяя.дпу, ЬуяТепрогг) 11ягепЯос«.ЬЬяТеп( СоппесТОпепеьепчТЬ ); // Ожидать дескрипторов соединений. Гог( гпТ 1 = 01 1 < МахСоппесгтопНапо1егя) ++1 ) ( 11згепзос«.ВедтпАссерТ( пен АяупсСа11Ьас«(ЕпггуРо1пТ.Напб1еСоппесТЬоп), 11ягепБос« ) ] Сопяо1е.иг1геЬЬпе( "Нажмите <Епгег> дпя завершения" Сопзо1е.кеаоЬ1пе О; Теперь сервер "Не!1о %(ог!б!" полностью использует пул потоков процесса и может обрабатывать входящие клиентские запросы с максимальной степенью параллельности.
Кстати, протестировать соединение весьма просто, если использовать встроенный в ЪЧ!п4]оьчэ клиент апет. Просто запустите Те!пег из командной строки или из диалогового окна запуска программ, и введите команду для подключения к порту 1234 локальной машины при запущенном в другом командном окне процессе сервера: М1сгозогг Те1пег> ореп 127.0.0.1 1234 410 Глава 12 Таймеры Еще одна точка входа в пул потоков находится в объектах класса Ттшег из пространства имен яуясеш.
ТЬгеаг)1пс. С его помощью можно настроить пул потоков на вызов делегата в определенное время или через регулярные интервалы. Рассмотрим пример использования объекта Ттшег: пягпя Яуягеш; пягпч Яуясеп.ТЬгеааьпон рпЬ11с с1аяя ЕпсгуРотпс ( ргттасе ясастс чогс Т1шегргос( оЬ)есс ясасе ) ( Сопяо1е.иггсеьгпе( "текушее время (О) иа потоке (1)", Оасетгпе.нои, Тпгеао.спггепстпгеап.Мапачестпгеапто ); Тпгеап.Я1еер( 3000 ); ) ясастс но1о Ма1п() ( сопяо1е.игггеьгпе( "нажмите <епгег> лля яавершения1ып" ) Т1шег шутгшег = пее Т1шег( пеи Т1шегса11Ьаск(Епггурогпс.тгшегргос), пп11, О, 2000 ); Сопяо1е.кеаоъгпе()л шуТгшег.
01ярояе (); ) ) При создании таймеру необходимо предоставить делегат, который должен быть вызван в заданное время. Для этой цели создан делегат Т1шегСа11Ьаск, указывающий на статический метод Т1шегргос. Вторым параметром конструктора Тмпег является произвольный объект состояния, который можно передать ему. Когда происходит обратный вызов таймера, ему передается этот объект состояния.
В рассматриваемом примере объект состояния не нужен, поэтому на его месте передается пп11. Последние два параметра конструктора определяют, когда должен произойти обратный вызов. Предпоследний параметр указывает, когда таймер должен сработать в первый раз. Передача значения 0 говорит о том, что таймер должен быть запущен немедленно. Последний параметр— период, через который должен произойти следующий вызов.
В примере запрошен двухсекундиый период. Если таймер не должен запускаться периодически, в последнем параметре необходимо передать Т1шеопс. 1пт1п1ге. И, наконец, для останова таймера нужно вызвать его метод 01ярояе. Может возникнуть вопрос о том, зачем производится вызов Я1еер внутри метода Т).шегргос. Это делается просто в целях иллюстрации, для того, чтобы показать, что Тгшегргос вызывает произвольный поток. Поэтому любой код, который выполняется в результате запуска делегата Т1шегСа11Ьаск, должен быть безопасным в отношении потоков.
В рассматриваемом примере первый поток из пула, вызывающий Т1шегргос, находится в спящем состоянии дольше, чем длительность следующего таймаута, поэтому пул потоков вызывает Ттшегргос двумя секундами позже из другого потока, как видно из сгенерированного вывода. Увеличив период сна в ттшегргос, можно действительно вызвать некоторое напряжение в пуле потоков. й)ногопоточность в СЗ 41 1 На заметку! Если когда-либо приходилось использовать класс тзтег из пространства имен Яузгею.
Изпоонэ. Яогтз, то должно быть понятно, что он существенно отличается от класса тткег из пространства яузсегл. тпгезптп9. например, Гогпэ. ттпег основан на системе обмена сообщениями уу!п32, а именно — на сообщении ХН ТТНЯЯ. Одно из удобств Гоггя. Тзпег заключается в том, что обратный вызов этого таймера всегда происходит в том же потоке. Однако обязательным условием работы такого таймера является то, что поток графического интерфейса, частью которого является этот таймер, должен иметь лежащее в его основе средство подкачки сообщений графического интерфейса. Если средство подкачки останавливается, то же самое происходит и с обратными вызовами Рога.
Тьпег. Поэтому, естественно, тпгеаптпд. ттпег — более мощный инструмент в том смысле, что он не страдает такой зависимостью. Однако его недостатком можно считать необходимость кодирования обратных вызовов для тпгеаг)тп9. тттег в безопасной в отношении потоков манере. Параллельное программирование На протяжении 80-х годов большинство персональных компьютеров имело только один процессор, но с выходом новых процессоров скорость их работы постоянно росла.
Повышение производительности процессоров обеспечивало повышение производительности приложений независимо от того, были они однопоточными или многопоточными. Однако в настоящее время, когда кривая роста производительности процессоров стала более пологой, рост мощности процессоров обеспечивается большим количеством ядер. На момент написания этой книги компания 1п1е1 выпустила на рынок процессор с восемью ядрами.
Чтобы воспользоваться выигрышем в скорости, который обеспечивает добавление в систему процессоров или ядер, необходим другой стиль разработки программного обеспечения, с упором на параллелизм. Естественно, параллельные приложения должны быть в высокой степени многопоточными, а потому чрезвычайно сложными. Их разработка занимает больше времени и требует повышенное внимание к деталям; то же самое касается и тестирования таких систем. Чтобы сократить дополнительную сложность. необходимы гибкие и тщательно отлаженные библиотеки, облегчающие решение задач параллельного программирования. Команда Рагайе1 Сошрп!!пл Р1ат!опп (Платформа параллельных вычислений) из М1сгоэой разработала расширения Рата)!е! Ех!епз1опэ и библиотеку Таз)г РагаИе! Ь!Ьгагу (ТРЬ), которая включена в .
НЕТ 4.0 ВСЬ. На заметку! Для ознакомления с деталями параллельного или свободного от блокировок программирования при разработке как "родных", так и . НЕТ-приложений, настоятельно рекомендуется прочитать книгу Джо Даффи (дое Ообу) Солсиггел! Ргодгзглгп!лд ол НГ!лдоткз (Абсйоп УУез!еу, 2009 г.). Библиотека ТРЬ значительно сокращает объем работы, которую необходимо выполнить для взаимодействия с пулом потоков и создания надежных параллельных программ. Параллельные программы, написанные на основе ТРЬ. также автоматически масштабируются на платформах с множеством процессоров, не требуя перекомпиляции.
Библиотека ТРЬ включает пространства имен Яузееп.тигезозпд и Яузееа.ТПгеаЖпд.Тазкз. Полное описание расширений Рагайе! Ехтепв!опз и библиотеки ТРЬ выходит эа рамки настоящей книги, но ниже дается краткое описание, которое позволит уловить суть этих средств. На заметку1 При использовании Рагайе! Ех1епзюпз и ТРЬ для определения различных делегатов, передаваемых библиотеке, более естественным и выразительным представляется использование лямбда-выражений. Если вы недостаточно уверенно оперируете лямбда-выражениями, то сначала прочтите главу 15. 412 Глава 12 Класс таз)с Основным классом в библиотеке ТРЬ является Тая)с.
В разделе "Асинхронные вызовы методов" ранее в главе было показано, как применять вызовы Ведап1пчо)се и Епс(1пчо)се для планирования асинхронного запуска работы в пуле потоков СЬК. Класс тая)с дополнительно облегчает эту работу, обеспечивая даже еще более высокую эффективность, чем при использовании пула потоков СЬК. При разработке ТРЬ команда Рагайе! Согпрпцпя Р)а()опп приложила достаточно усилий по исключению как можно большего количества блокировок, полагаясь на неблокирующие приемы синхронизации. Чтобы получить представление о том, что можно сделать с классом таз)с, вернемся к ранее рассмотренному примеру с сокетом. Ниже приведена модификация примера асинхронного совета, который принимает только одно подключение и после установки соединения просто посылает клиенту строку "НеБо срог)сГ.










