Нэш Трей - C# 2010. Ускоренный курс для профессионалов (2010) (1160865), страница 110
Текст из файла (страница 110)
пя(пд Буягев; пя1пд Бувгев.Техгс пзапд Буягев.т)сгеас)спд; пявпд Буягев.тпгеас)1пд.тав)сяс пзапд Буягев.кеес пя1пд Буягев.нег.зосуегя) рпЫ1с с1аяя Епггуроапг ( рг1часе сопев 1пс соппессОпеиеьепдсй = 44 ргсчаге сопев (пг Ь(ягепРогг = 1234; ягагас чо(С Ма1п() ( чаг с)опеЕчепг = пе» Маппа1хеяеСЕчепСБ11в(); /l Создание задачи для прослушивания сокета.
Бес)сеС 11вгепзосЕ = пе» Бес)сег( Ас(с)геяярав11у.1пгегнег»ог)с, Бес)сегтуре.зггеав, Ргогосо1Туре.Тор ); Бес)сев соппесгаоп = пи11) сгу ( Тазв 11есепуаев = пп11; 1хвеептав1с тае)с.уасеову.зеавеие»( () => ( 11еселвосХ.ВТлб( пе» ХРЕпс(ро1пс(ХРдсЫвевв.дпу, Ь1зсепровс) ); 1весепвосХ.Ьаесел( Соппесссспепеьепдсв ); соплесе1оп = 11всепвос1с.лссерс()) 11ееептав)с.сопе1плеи1еь( (рхе»холзтаз1с) => ( Ьусе[! швд = Епсос(1пд.ПТРЗ.деввусее( "Не11о Иое1с(1" )) соппессаоп.валс)(шед, Зос)сеЕР1адв.ноле )) соппессхоп.С1ове(); с(опек»еле.зев()) ) )) сопяо1е.хгасеьапе( "Ожидание завершения задачи..." )) СопеЕчепг.каЬС(); ) сапов( ХддгедагеЕхсергаоп е ) ( Сопяо1е.кг(геЬ1пе( е ); 11вгепзос)с.С1ояе О с ) ) В этом примере следует обратить внимание на несколько моментов. Многопотонность я СЗ 413 1.
Лямбда-выражения позволяют гибким образом выразить то, что требуется получить от задачи, позволяя на лету создать делегат и передать его методу Таяхуассогу.ЯГагсиеэ(). 2. Первый экземпляр Тая)г — 11ясептаяк — обрабатывает операции прослушивания входящих соединений, и он создается с помощью фабрики таякуассогу, доступной через статическое свойство Тая)г. Рассогу.
Можно было бы сначала создать экземпляр тая)г, а затем вызвать Ясагг на нем, однако, если вас нет причин поступать именно так, то лучше воспользоваться методом Тая)суассогу. Ясагсиеж так как он более эффективен. Но обратите внимание, что экземпляр задачи соединен с СопсуппеХТГЬ на начальной задаче и передан другому экземпляру Тая)г. Метод Сопс1ппеИТГЬ позволяет легко соединять экземпляры ТаяМ между собой. Существуют и другие перегрузки Сопс1ппеХТГЬ, описание которых приводится в документации МЕР)А), а по умолчанию этот метод запланирует очередную непрерывную задачу после завершения предыдущей.
Кроме того, Соп11ппек1ГЬ передает экземпляр предыдущей задачи Тая)г делегату непрерывного экземпляра Таях, В данном случае необходимо, чтобы непрерывная задача послала строку "Не11о Р))ог16" через совет, как только будет установлено соединение. 3. Использование Таях помещено в блок Сгу/сассЬ, где сассЬ перехватывает исключения типа АддгедасеЕхсерс1оп. Тип АддгедасеЕхсер11оп — это новый тип, представленный в ТРЬ и предназначенный для перехвата исключений от множества асинхронных и взаимосвязанных задач.
Это понадобилось потому, что и 11ясептаяк, и непрерывная задача могут генерировать исключения. Таким образом, АддгедасеЕхсерг1оп позволяет перехватывать набор исключений . Класс Рага11е1 пя1пд П51пд 05 1пч пя1пд пя1пд с1аяя ) Яуягеп) Яуясеь.кптег1ся) Яуягеп.ТПгеапзпч) Яуясек.ТПгеаагпЯ.Таяхя; Яуясеп.СП11ессзопя.Сопспггепс) ЕпсгуРо1пс попас Тпс Рассогза1ятосопрпсе = 2000) Дополнительные сведения о типе АЕЕгеда1еЕхсерпоп ищите к статье Стивена Тауба )ВГерЬеп ТапЬ) Аддгедакпд Ехсероопя в августовском номере журнала МЯЮА) Модах)пе за 2009 с Класс яуягеи. ТЬгеасНП9.
Рага11е1 содержит набор статических методов, с помощью которых можно легко трансформировать традиционную непараллельную задачу в параллельную. Например, в нем предусмотрены методы, которые применяются для замены циклов Рог или Тогеасп параллельными циклами Рог, когда вместо итерации по каждому элементу в последовательной манере каждая итерация передается в пул потоков, где выполняется параллельно с другими.
Помимо этого, методы класса Рага11е1 позволяют делать и многое другое, но детальное описание устройства и работы расширений Рага11е) Ех)епа1опя для платформы .)))ЕТ выходит за рамки настоящей книги. Разумеется, далеко не все циклы Рог и Тогеасн можно вслепую заменять параллельными циклами Рог. Для этого код внутри цикла должен допускать параллельное выполнение, и каждая итерация должна быть достаточно независимой от всех остальных.
Рассмотрим пример, демонстрирующий, насколько непростой может оказаться задача введения параллелизма. Приведенный ниже код, который выполняется последовательно, будет превращен в параллельный. 414 Глава 12 ягяс1с чогб маги() ( чяг ипыпегя = пен Сспспггеп101сс1спягу<В101пгедег, В101пседег>(4, Гяссог1я1ятосоарисе)( О Создат~ делегат для вычисления фякторняла. Гипс<В101пьедег, В101пгедег> Гасгогта1 = пп11( Тасгог1а1 = (п) => ( п == 0 ) ? 1: п * Гаспог1я1(п-1); // Вычислить фяктсрналы для списка значений. Тот( в1оид 1 = О( 1 < Гясгог1а1ятсссарпге; ++1 ) ( ипапегя (1] = Тясссгта1(г); ) В этом примере кода вычисляются первые 2000 факториалов, а результат сохраняется в словаре.
Операции факториала соответствует достаточно сложный алгоритм, предусматривающий рекурсивные вычисления, поэтому в данном примере стек не должен перегружаться. Внимание! Будьте осторожны с рекурсивными вычислениями вроде получения факториала, потому что есть риск перегрузить стек настолько, что это приведет к исключению ЯсасХОчег11оиВхсерс1оп. В главе 1б будет описана концепция мемоизации (п(еп(о(габоп; также называемая "запоминанием"), которая позволяет избежать такой ситуации в рекурсивных функциях, подобных вычислению факториала.
Вычисление факториала в том виде, как приведено выше, быстро переполнит любой из встроенных целочисленных типов. поэтому пришлось использовать тип В101ппедег, представленный в .]ЧЕТ 4.0. Также обратите внимание, что для хранения результатов создан экземпляр Сопспгепгргспфопагу — одного из типов коллекций пространства имен Яуягеы. Со11есптоп.
Сопспггепг, появившегося в .]4ЕТ 4 О. Параллельные коллекции оптимизированы для параллельной работы, поскольку они используют блокировки очень консервативно и только в случае необходимости, что повышает производительность и сокращает состязания в параллельных средах. Несмотря на то что приведенный выше код не параллелен, с прицелом на будущие модификации в нем применяется тип Сопспгеппрфсггопагу.
Если запустить приведенный выше пример, то можно увидеть, что он выполняется, как и следовало ожидать, однако при внимательном рассмотрении обнаруживается, что каждая итерация цикла Тот независима от остальных, что делает его очевидным каь(дндатом на внедрение параллелизма. Ниже приведен тот же код, но с добавлением параллелизма с помощью Рага11е1. Гог: ия1пд Яуяпего иягпд Яуякеп.нпаеггся( пя1пд Яуяяеа.тпгеяс1пд( иягпд Яуягеп.ТЬгеас1пд.Тяякя( иягпд яуягеа.со11есг1опя.сопспггепг( с1яяя ЯпягуРстпп сопяс гпг Гассог1а1ятосоарпсе = 2000) ягаггс чо1г( Маги() чаг ииапегя = пен сопспггепгО1сггопагу<В>д1пгедег, В1д1пгедег>(4, Гасгсгга1ятосоприсе)( // Создать делегат для вычисления фякториала.
Гипс<В>01пгедег, В1дтпгедег> Гяссогтя1 = пп11( Гасгог1я1 = (и) => ( и == 0 ) ? 1: и * Тясссг1а1(п — 1); )(ногопоточность з СЕ 4 16 // начислить Еакторизли лля слиска влачалий лараллельво. Вала11е1.вол( О, тасеотаа1втоссириее, (Цл>( поивевв [Ц л Гаоеоава1(Ц; ) )г Кзк видите, метод Рага11е1.Рог позволяет очень легко выполнить необходимую трансформацию. В первыхдвухаргументахуказывается диапазон итерации, автретьем — делегат, который в данном случае является лямбда-выражением.
представляющим тело исходного цикла гог. Просто, не правда ли? Но не спешите! После запуска показанного выше кода оказывается, что он выполняется намного медленнее, чем его последовательная версия. Компиляция двух предыдущих примеров кода и запуск их параллельно на четырехядерном сервере Нурег-1/ под управлением йг!пбоиз 7 с анализом посредством крег(ч дает результат, показанный на рис. 12.5. Рис. 12.6.
Анализ с помощью хрег( примеров последовательного и параллельного вычисления фвкторивпв Два графика на рис. 12.5 представляют использование процессора на процесс и на поток. В нижнем графике прямоугольный импульс слева представляет единственный поток, который вычисляет 2000 фзкториалов в последовательной версии кода. Соседняя часть графика, которая выглядит как неупорядоченный шум, представляет множество потоков в параллельной версии кода. Хотя теперь фзкториалы вычисляются множеством потоков, полученное поведение определенно не устраивает. В конце концов, параллельная версия должна работать быстрее, верно? На приведенном графике видно, что она выполняется не просто дольше последовательной, но дольше более чем в четыре раза! Также обратите внимание, что использование процессора в обеих версиях, как показано на верхнем графике, было относительно низким.
Так в чем же дело? Если вы незнакомы с хрегь настоятельно рекомендуется изучить его, посетив сзйт йг)пбоиз Регтоппапсе Тсо1з (%РТ) КЫ по адресу Псер: //гззг)п.пзсгозогг. сот/регйогпапсе, Хрегг — исключительно мощный контроллер и средство просмотра трассировки событий для йгвтбоиз (етеп1 тгасвтл (ог %гшбоиз (ечъ7)).















