Нэш Трей - C# 2010. Ускоренный курс для профессионалов (2010) (1160865), страница 111
Текст из файла (страница 111)
416 Глава 12 В ситуациях подобного рода для получения более ясной картины необходимо применять инструменты вроде хрегГ или монитора производительности Бузтеш Рет1оппапсе Моптгог (регйпоп). Для диагностики таких проблем в СЬК доступно сравнительно немного счетчиков производительности. Поэтому чтобы лучше понять происходящее, следует запустить параллельную версию кода с ведением журнала производительности. Результат можно видеть на рис.
12.6. Рис. 12.6. Янализ параллельного кода вычислении факториала с помощью регйпоп На этом рисунке сплошная линия вверху представляет процент времени, потраченного сборщиком мусора СЬК, а пунктирная линия — объем генерации 0 коллекций. Таким образом, можно заключить, что проблемы производительности параллельной версии вызваны сборщиком мусора (ОС).
Это предположение имеет смысл, если подумать. насколько интенсивно используется тип В1с1пгесег при создании и уничтожении множества экземпляров В1с1псесег в каждой итерации, что сказывается на нагрузке СС. Поскольку в параллельной версии кода поддерживается не один, а множество потоков, загружающих ОС, проблема еще более усугубляется.
Что же делать в такой ситуации? Решение оказывается неожиданно простым. Поскольку проблема связана с СС. его необходимо настроить для более производительной работы с параллельным кодом. Это делается за счет использования серверной версии СС вместо стандартной версии для рабочей станции. Сборщик мусора — одна иэ тех областей СЬК, которая непрерывно развивается в сторону повышения эффективности и многогранности. Серверный ОС— результат усилий, предпринятых разработчиками для повышения параллелизма, который часто нужен серверным приложениям. Фактически серверный СС является стандартным сборщиком мусора для приложений АЗРХЕТ.
Но как его включить? Все, что для этого понадобится — это создать конфигурационный ХМ1 файл приложения по имени <арр11сасаоп>. сопг1р, где <арр11сасаоп> заменяется полным именем исполняемой программы, включая расширение. Например, конфигурационный файл для Рага11е1. ехе должен иметь имя рага11е1. ехе. сопГЬФ Включить серверный ВС можно с помощью элемента ссБегтгег следующим образом: <соп11оогзг1оп> <гипг1ае> <ОоаэктГЕХ Еаав1Ебе"Сказ" /> <Ггспсзае> <усопттдпгасаоп> цногопоточность в О» 411 После включения серверного ОС вы увидите заметное повышение производительности при выполнении параллельной версии кода.
Эго произойдет потому, что серверный сборщик мусора для каждого процессора создает кучи ОС, которые лучше подходят для параллельного выполнения. Вдобавок операционная система ЪИпбои7в старается насколько возможно часто планировать потоки на один и тот же процессор.
Зто сокращает объем блокировок, которые потребуются для работы ОС. На рис. 12.7 показан вывод хрегт", захваченный при выполнении последовательной и параллельной версий кода вычисления факторивла при наличии показанного выше конфигурационного файла. Обратите внимание, что после включения серверного ОС параллельная версия требует гораздо меньше времени на выполнение вычислений.
Вдобавок параллельный процесс намного интенсивнее использует процессор, чем последовательная версия, что также желательно, поскольку как раз и является одной из целей параллельного программирования. Отвлекаясь от данного эксперимента, следует отметить, что введение параллелизма в приложения редко дается столь же легко.
Обычно приходится принимать во внимание факторы среды и подготавливать тесты для анализа производительности, позволяющие удостовериться, что параллельное выполнение действительно дает выигрыш в производительности. Рис. 12.7. Анализ с помощью хрегт последовательного и параллельного вычисления фвкториапв с включенным серверным ВС Простой вход в пул потоков В разделе "Использование ТпгеабРоо1" ранее в главе было показано, как планировать асинхронную работу с пулом потоков СЬК, применяя для этого тпгезбРоо1. Яиесепзегиогк1геьь и как использовать Ведтп1пчохе и Кпгг1пвоке на делегатах для получения входа в пул потоков.
Однако, начиная с .ХЕТ 4.0, для решения задач в стиле "сделал и забыл" появилась возможность более простого входа в пул потоков — с помощью Рага11е1.1птоке. Запуск асинхронных задач через потоки из пула потоков СЕВ очень легко и успешно планировать. передавая массив делегатов Асс»оп методу Рзга11е1. 1пчохе. 418 Глава ) 2 Затем Рага11е1. 1пчо)се блокируется до завершения всех действий.
Рассмотрим следующий пример: пягпд Яуясеяп оягпд Яуясет.кпиегтся) пзгпд Яуягеи.тпгеа<(1пд; оягпд Яуягез.тпгеаоспд.тая)ся; нягпд яуяпет.со11есг[опя.сопспггепС; с1аяя Ептгурогпо ( сопяС гпС Гасгог1а1зтосотрппе = 100; ягаягс чо1с) Магп() ( чаг ппиЬегя = лен Сопсоггепгптсс1опагу<5101пяедег, Вгд1ппедег>(); // Создать делегат для вычисления фахтариала.
Гипс<В[01ЬСедег, В101пяедег> Таспогта1 = пп11; Тасгогта1 = (и) => ( и == О ) Т 1: и * Гасгогса1(п-1) // Построить массив действий. чаг асггопя = лен йсС1оп[Гастог1а1втосоирпге]; гог( гпс г = 0~ г < Гассогга1ятосовросе) ++1 ) ( гпС х = г; асС1опя[1] = () => ( ппиЬегя[х] = Таспогга1(х)) // Вычислить значения. Рага11е1.1пчохе( асС1опя ) // Вывести результаты. Тот( гпс 1 = О; г < Гассогта1втосотросе) ++1 ) ( Сопяо1е.вг1Се11пе( позоегя[1) ); ) ) ) Зтот пример является вариацией примера Рага11е1. Гог, за исключением того, что вместо использования Рага11е1.
Гог создается массив )ссс1опя, который затем передастся в Рага11е1. 1пчо)се. Однако имейте в виду, что в случаях. подобных данному, Рага11е1. Гог обычно работает лучше, а 1пчо)<е используется только для примера. Чтобы увидеть разницу, рекомендуется провести анализ производительности, как было описано выше, сравнив производительность Рага11е1.
1пчо)<е и Рага11е1. Гог. Для каждого вычисления факториала было построено свое действие АСС1оп, которое помещено в массив. Затем этот массив передан в Рага11е1. 1пчо)<е. Как только все это сделано, и вызов Рага11е1. 1пчо)<е вернул управление, на консоль выводятся результирующие значения. И последний момент, который следует отметить; нет никаких гарантий относительно порядка выполнения задач, переданных Ра ха11е1.
1пчо)<е, как нет гарантий и того, что они на самом деле будут выполняться параллельно. Все зависит от платформы, на которой выполняется код, и параметров среды, учитываемых Рага11е1 . 1пчо)се. Классы коллекций, безопасные в отношении потоков Выше были наглядно продемонстрированы все сложности и трудности, которые связаны с созданием корректного безопасного к потокам кода. На этом пути вас подстере- Многопоточность в С№ 419 гает множество ловушек.
Более того, код будет эффективнее, если вы будете стараться применять насколько возможно легковесные из доступных механизмов синхронизации. Тем не менее, свободное от блокировок параллельное программирование, при котором блокировки не используются явно, а задействуются средства синхронизации платформы — не такая уж умопомрачительно сложная задача. Разумеется, такой код требует разнообразного тестирования во всех средах, чтобы были отловлены все возможные ошибки, связанные с параллелизмом.
Всякий раз, когда для выполнения работы можно применить предоставляемый библиотекой механизм, не пренебрегайте этим. Например, следует избегать кодирования собственной реализации пула потоков. Аналогично. когда понадобятся синхронизованные классы коллекций, обратитесь к классам из пространства имен Яуятеа.Со11ест1опя.Сопспггепт,предлагаемого .НЕТ 4.0. Коллекции из Вуятев.со11ест1опя.Сопспггепт используют наиболее эффективный механизм блокирования из всех возможных, поэтому беспокоиться о создании собственного механизма не нужно. Когда возможно, они применяют свободный от блокировок доступ к содержимому, а когда необходимо — легковесную, мелкомодульную блокировку.
Настоятельно рекомендуется изучить типы, определенные в пространстве имен Вуятев.со11есгтопя.сопспггепг, и отдавать им предпочтение при работе над параллельными приложениями. В пространстве имен вы найдете такие типы, как сопсоггепгяиеое<т>, сопсоггепгягзсн<т>, сопспггепгВао<т> и тп. Резюме В этой главе были раскрыты тонкости управляемых потоков в среде .НЕТ.
Помимо прочего, рассматривались различные механизмы обеспечения управляемой синхронизации между потоками, включая 1птег1оснеб. Моп1тог, АптоаеяеГЕчепт, мапоа1ееяегегепг, объекты на базе юаггнапо1е и тп. Кроме того, был описан шаблон 1ОУ и показано, каким образом .НЕТ РгашеаюгК интенсивно использует его для выполнения асинхронной работы. Это обсуждение было сосредоточено вокруг использования средой СЬЙ класса Тпгеаброо1, основанного на реализации пула потоков йЧпбогуз. Вдобавок было показано, насколько облегчается решение задач параллельного программирования с помощью появившихся в .НЕТ 4.0 расширений РагаПе1 Ехтепэ1опэ и библиотеки ТР1..
Многопоточность всегда усложняет приложения. Однако при правильном использовании она может сделать приложения более отзывчивыми на команды пользователя и более эффективными. Хотя многопоточная разработка не лишена некоторых ловушек, .НЕТ РгшпеаюгК и СЬК максимально смягчают все риски и предоставляют модель, которая в большинстве случаев защищает от сложностей низкоуровневой работы с операционной системой.
Например, реализация пулов потоков всегда была трудной задачей. даже после того, как общая реализация была добавлена в операционную систему ЪУ1пбочгя. Среда . НЕТ не только является замечательным буфером между кодом и сложным пулом потоков ЪУ1пбочяэ, но также позволяет запускать код на других платформах, реализующих .НЕТ РгашевогК, таких как исполняющая среда Мопо для 1Лпих. Если вы понимаете детали средств многопоточности, предоставленных .НЕТ, и знакомы с приемами многопоточной синхронизации, описанными в этой главе, то вы на верном пути к производству эффективных многопоточных приложений. Следующая глава посвящена поиску канонической формы С№ для типов.
Будет составлен перечень вопросов, которые вы, как разработчик, должны задать себе при проектировании типа с использованием С№ для .НЕТ РгашевогК. глдвА 13 В поисках канонических форм С6 М ногие объектно-ориентированные языки, включая С№, никак не принуждают разработчиков создавать хорошо спроектированное программное обеспечение. Лучший пример тому — применение С++ для реализации объектно-ориентированного дизайна. Язык С№ несколько более структурирован, чем С++; например, он не допускает создания свободных статических функций, находящихся вне контекста определенного типа. Тем не менее, С№ не заставляет создавать программное обеспечение, которое следует широко известным принципам хорошего дизайна программ. В сообществе С++ быстро идентифицировали некоторые канонические формы, полезные для проектирования типов, предназначенных для определенной цели.
В действительности эти канонические формы представляют собой просто списки или рецепты, которые можно использовать при проектировании новых классов. Прежде чем вывести самолет на взлетную полосу, пилот обязан пройти строгую процедуру, ответив на ряд вопросов. Цель настоящей главы — идентифицировать списки таких вопросов, которые позволят создавать надежные типы в мире С№. При открытии списков вопросов подобного рода следует учитывать, какого рода поведение требуется от объектов создаваемого нового типа. Например, долнсен ли новый тип поддерживать клонирование? Если экземпляры нового типа помещаются в коллекцию, могут ли они упорядочиваться? Что означает сравнение на эквивалентность двух ссылок на объекты этого типа? Другими словами, нужно лн знать о том, что две ссылки указывают на один и тот же экземпляр? Или же необходимо знать, что два экземпляра находятся точно в одном и том же состоянии? Вопросы подобного рода всегда должны задаваться перед созданием нового типа.











