Читайте также:
|
|
Параллелизм задач (task parallelism) – это подход наиболее низкого уровня для распараллеливания задач с помощью PFX. Классы этого уровня определены в пространстве имен System.Threading.Tasks.
По сути, задание представляет собой легковесный объект для управления распараллеливанием единицы работы (unit of work). Задание избегает накладных расходов по запуску выделенного потока путем использования пула потоков CLR: тот же самый пул потоков используется при вызове функции ThreadPool.QueueUserWorkItem, настроенный в CLR 4.0 специальным образом для более эффективной работы вместе с заданиями (и более эффективной работы в целом).
Задания могут применяться всегда, когда вам нужно выполнить что-либо параллельно. Однако они оптимизированы для повышения эффективности многоядерных процессоров: фактически класс Parallel и PLINQ построены на основе конструкций параллелизма задач.
Задания делают значительно больше, нежели предоставляют эффективный способ использования пула потоков.
PFX представляет базовую форму структурного параллелизма с помощью трех методов класса Parallel:
Parallel.Invoke: выполняет параллельно массив делегатов
Parallel.For: параллельный эквивалент цикла for
Parallel.ForEach: параллельный эквивалент цикла foreach
Все три метода блокируют управление до окончания выполнения всех действий. Как и при использовании PLINQ при возникновении необработанного исключения, оставшиеся рабочие потоки прекращают выполнение после завершения обработки текущего элемента и исключение (или исключения), завернутые в AggregationException, пробрасываются вызывающему коду.
Параллельные коллекции иногда бывают полезны при решении обычных задач многопоточности, когда вам нужна потокобезопасная коллекция. Однако есть несколько пояснений:
Параллельные коллекции оптимизированы для параллельного программирования. Стандартные коллекции их превосходят во всех случаях, кроме сценариев с высокой конкурентностью.
Потокбезопасные коллекции не гарантируют, что код, который их использует будет потокобезопасным.
Если в процессе перебора элементов параллельной коллекции другой поток ее модифицирует, исключение сгенерировано не будет. Вместо этого, вы получите смесь старого и нового содержимого.
Не существует параллельной версии List<T>.
Параллельные классы стека, очереди и набора (bag) внутри реализованы на основе связных списков. Это делает их менее эффективными в плане потребления памяти по сравнению с непараллельными версиями классов Stack и Queue, но более предпочтительными для параллельного доступа, поскольку связные списки являются отличными кандидатами для lock-free или low-lock реализаций. (Поскольку вставка узла в связный список требует модификации лишь пары ссылок, в то время как вставка элемента в структуру данных наподобие List<T> может потребовать перемещения тысяч существующих элементов.)
Другими словами, использование этих коллекций не эквивалентно использованию стандартных коллекций с операторами lock. Например, если мы выполним этот код в одном потоке:
var d = new ConcurrentDictionary<int,int>();
for (int i = 0; i < 1000000; i++) d[i] = 123;
он будет выполняться втрое медленнее, нежели этот код:
var d = new Dictionary<int,int>();
for (int i = 0; i < 1000000; i++) lock (d) d[i] = 123;
(Однако чтение ConcurrentDictionary выполняется быстрее, поскольку чтения являются lock-free.)
Параллельные коллекции еще отличаются от стандартных коллекций тем, что они содержат специальные методы для выполнения атомарных операций типа «проверить-и-выполнить» (test-and-act), такие как TryPop. Большинство этих методов унифицированы посредством интерфейса IProducerConsumerCollection<T>.
28. Средства обобщенного (generic) программирования в языках C++ и C#.
Общие (или параметризованные) типы (generics) позволяют при описании классов, структур, методов и интерфейсов использовать параметризованные параметры (не указывать тип параметра в момент написания кода). Тип параметра определяется в момент объявления переменной соответствующего типа. Таким образом, можно создать некоторый общий элемент, тип который можно использовать в дальнейшем для данных различных типов. Программисты на C++ могут углядеть с общих типах сходство с шаблонами (templates), в чем-то эта аналогия будет верна.
Какие же преимущества дает использование общих типов?
Наиболее очевидное - повторное использование кода. Нет необходимости создавать два идентичных класса, отличающихся только типами параметров, достаточно создать один с параметризованными типами. При этом использование параметризованных типов позволяет создавать единый программный код для работы с различными типами данных. Например, единожды написанный алгоритм может работать и с целыми числами и с числами с плавающей десятичной точкой, при этом, не производя на каждом шаге проверку/приведение типа. Так Generics вытесняют классы объявленные с использованием типа object.
Повышение производительности кода по сравнению с использование параметров типа object - нет необходимости выполнять приведение, как уже сказано выше, на каждом шаге, за счет чего получается выигрыш в производительности.
Проверка типов в момент компиляции программы. Поскольку не используются параметры типа object, то компилятор может выполнить проверку типа каждого параметра в момент компиляции, поскольку типы для Generic классов жестко задаются в момент объявления переменных классов этого типа.
К сожалению, опытных программистов на C++ я должен несколько расстроить. Общие типы все-таки не соответствуют шаблонам в C++, поскольку параметризованные типы в C# не могут иметь типов по умолчанию. Параметризованные типы не могут быть использованы в качестве базовых классов для общих типов. Также в C# не допускается использования Generic классов в качестве параметров типов в других Generic классах.
Допускается применение шаблонов с целью реализации абстрактных алгоритмов, то есть шаблонов функций. template <class T> void sort(vector<T>& v); При вызове шаблонных функций компилятор подставляет тип данных и создает новый вариант функции. Если один и тот же тип данных используется несколько раз, то на все типы данных создается один шаблон функции. При использовании шаблонов существует три больших недостатка: * шаблоны невозможно отлаживать. Разработка шаблонов обычно ведется так: разрабатывается класс или функция конкретного типа данных; этот класс или функция параметризуются, то есть создается шаблон. * существенно замедляется время компиляции. В больших проектах оно может доходить до 30-60 минут. * очень быстро растут размеры объектных модулей и библиотек на диске. Компиляция относительно большого проекта в отладочном режиме может потребовать до 10 ГБ. Можно объявить несколько шаблонов функций с одним и тем же именем, а так же можно объявить комбинацию шаблонов и обычных функций с одинаковым именем. Шаблонные функции могут вызываться с явным указанием параметра шаблона: sqrt<int>(2); или без него: sqrt(2);
Как видно из примера, для того чтобы использовать общие типы нужно после объявления класса указать параметризованные типы: Generics<TYPE1, TYPE2> объявляет класс с двумя параметризованными типами. Более того, параметризованные типы могут быть использованы при объявлении делегатов функций.
Обобщения (generics) — еще один механизм, поддерживаемый общеязыковой исполняющей средой (CLR) и языками программирования, который является новой формой повторного использования кода, а именно повторным использованием алгоритма.
По сути, разработчик определяет алгоритм, например сортировку, поиск, замену, сравнение или преобразование, но не указывает типы данных, с которыми тот работает. Поэтому алгоритм может обобщенно применяться к объектам разных типов. Используя готовый алгоритм, другой разработчик должен указать конкретные типы данных, например для алгоритма сортировки — Int32, String и т. д., а для алгоритма сравнения — DateTime, Versions...
Большинство алгоритмов инкапсулированы в типе. CLR поддерживает создание как обобщенных ссылочных, так и обобщенных значимых типов, однако обобщенные перечислимые типы не поддерживаются. Кроме того, CLR позволяет создавать обобщенные интерфейсы и обобщенные делегаты. Иногда полезный алгоритм инкапсулирован в одном методе, поэтому CLR поддерживает создание обобщенных методов, определенных в ссылочном, значимом типе или в интерфейсе.
В частности, в библиотеке FCL определен обобщенный алгоритм управления списками, работающий с набором объектов. Тип объектов в обобщенном алгоритме не указан. Используя такой алгоритм, нужно указывать конкретные типы данных.
■ Защита исходного кода Разработчику, использующему обобщенный алгоритм, не нужен доступ к исходному тексту алгоритма. А при работе с шаблонами C++ или обобщениями Java разработчику нужен был исходный текст алгоритма.
■ Безопасность типов Когда обобщенный алгоритм применяется с конкретным типом, компилятор и CLR понимают это и обеспечивают, чтобы в алгоритме использовались лишь объекты, совместимые с этим типом данных. Попытка использования объекта, не совместимого с указанным типом, приведет к ошибке компиляции или ошибке во время выполнения.
■ Более простой и понятный код Поскольку компилятор обеспечивает безопасность типов, в исходном тексте нужно меньше приведений типов, и такой код проще писать и поддерживать.
■ Повышение производительности До появления обобщений одним из способов определения обобщенного алгоритма было определение всех его членов так, чтобы они по определению «умели» работать с типом данных Object. Чтобы этот алгоритм работал с экземплярами значимого типа, перед вызовом членов алгоритма CLR должна была упаковать этот экземпляр. Приведения типов также не нужны (см. предыдущий пункт), поэтому CLR не нужно проверять безопасность типов при их преобразовании, что также ускоряет работу кода.
Дата добавления: 2015-01-30; просмотров: 14 | Поможем написать вашу работу | Нарушение авторских прав |