Чтение онлайн

ЖАНРЫ

Язык программирования C#9 и платформа .NET5
Шрифт:

Тем не менее, в некоторых случаях ручное управление потоками оказывается более предпочтительным. Ниже приведены примеры.

• Когда требуются потоки переднего плана или должен устанавливаться приоритет потока. Потоки из пула всегда являются фоновыми и обладают стандартным приоритетом (

ThreadPriority.Normal
).

• Когда требуется поток с фиксированной идентичностью, чтобы его можно было прерывать, приостанавливать или находить по имени.

На этом исследование пространства имен

System.Threading
завершено. Несомненно, понимание вопросов, рассмотренных в настоящей главе до сих пор (особенно в разделе, посвященном проблемам параллелизма),
будет чрезвычайно ценным при создании многопоточного приложения. А теперь, опираясь на имеющийся фундамент, мы переключим внимание на несколько новых аспектов, связанных с потоками, которые появились в .NET 4.0 и остались в .NET Core. Для начала мы обратимся к альтернативной потоковой модели под названием TPL.

Параллельное программирование с использованием TPL

Вы уже ознакомились с объектами из пространства имен

System.Threading
, которые позволяют строить многопоточное программное обеспечение. Начиная с версии .NET 4.0, в Microsoft ввели новый подход к разработке многопоточных приложений, предусматривающий применение библиотеки параллельного программирования, которая называется TPL. С помощью типов из
System.Threading.Tasks
можно строить мелкомодульный масштабируемый параллельный код без необходимости напрямую иметь дело с потоками или пулом потоков.

Однако речь не идет о том, что вы не будете использовать типы из пространства имен

System.Threading
во время применения TPL. Оба инструментальных набора для создания многопоточных приложений могут вполне естественно работать вместе. Сказанное особенно верно в связи с тем, что пространство имен
System.Threading
по-прежнему предоставляет большинство примитивов синхронизации, которые рассматривались ранее (
Monitor
,
Interlocked
и т.д.). В итоге вы на самом деле обнаружите, что иметь дело с библиотекой TPL предпочтительнее, чем с первоначальным пространством имен
System.Threading
, т.к. те же самые задачи могут решаться гораздо проще.

Пространство имен System.Threading.Tasks

Все вместе типы из пространства

System.Threading.Tasks
называются библиотекой параллельных задач (Task Parallel Library — TPL). Библиотека TPL будет автоматически распределять нагрузку приложения между доступными процессорами в динамическом режиме с применением пула потоков исполняющей среды. Библиотека TPL поддерживает разбиение работы на части, планирование потоков, управление состоянием и другие низкоуровневые детали. В конечном итоге появляется возможность максимизировать производительность приложений .NET Core, не сталкиваясь со сложностями прямой работы с потоками.

Роль класса Parallel

Основным классом в TPL является

System.Threading.Tasks.Parallel
. Он содержит методы, которые позволяют осуществлять итерацию по коллекции данных (точнее по объекту, реализующему интерфейс
IEnumerable<T>
) в параллельной манере. Это делается главным образом посредством двух статических методов
Parallel.For
и
Parallel.ForEach
, каждый из которых имеет множество перегруженных версий.

Упомянутые методы позволяют создавать тело из операторов кода, которое будет выполняться в параллельном режиме. Концептуально такие операторы представляют логику того же рода, которая была бы написана в нормальной циклической конструкции (с использованием ключевых слов

for
и
foreach
языка С#). Преимущество заключается в том, что класс
Parallel
будет самостоятельно извлекать потоки из пула потоков (и управлять параллелизмом).

Оба метода требуют передачи совместимого с

IEnumerable
или
IEnumerable<T>
контейнера, который хранит данные, подлежащие обработке в параллельном режиме. Контейнер может быть простым массивом, необобщенной коллекцией (вроде
ArrayList
),
обобщенной коллекцией (наподобие
List<T>
) или результатами запроса LINQ.

Вдобавок понадобится применять делегаты

System.Func<T>
и
System.Action<T>
для указания целевого метода, который будет вызываться при обработке данных. Делегат
Func<T>
уже встречался в главе 13 во время исследования технологии LINQ to Objects. Вспомните, что
Func<T>
представляет метод, который возвращает значение и принимает различное количество аргументов. Делегат
Action<T>
похож на
Func<T>
в том, что позволяет задавать метод, принимающий несколько параметров, но данный метод должен возвращать
void
.

Хотя можно было бы вызывать методы

Parallel.For
и
Parallel.ForEach
и передавать им строго типизированный объект делегата
Func<T>
или
Action<T>
, задача программирования упрощается за счет использования подходящих анонимных методов или лямбда-выражений С#.

Обеспечение параллелизма данных с помощью класса Parallel

Первое применение библиотеки TPL связано с обеспечением параллелизма данных. Таким термином обозначается задача прохода по массиву или коллекции в параллельной манере с помощью метода

Parallel.For
или
Parallel.ForEach
. Предположим, что необходимо выполнить некоторые трудоемкие операции файлового ввода-вывода. В частности, требуется загрузить в память большое число файлов
*.jpg
, повернуть содержащиеся в них изображения и сохранить модифицированные данные изображений в новом месте.

Задача будет решаться с использованием графического пользовательского интерфейса, так что вы увидите, как применять "анонимные делегаты", позволяющие вторичным потокам обновлять первичный поток пользовательского интерфейса.

На заметку! При построении многопоточного приложения с графическим пользовательским интерфейсом вторичные потоки никогда не смогут напрямую обращаться к элементам управления пользовательского интерфейса. Причина в том, что элементы управления (кнопки, текстовые поля, метки, индикаторы хода работ и т.п.) привязаны к потоку, в котором они создавались. В следующем примере иллюстрируется один из способов обеспечения для вторичных потоков возможности получать доступ к элементам пользовательского интерфейса в безопасной к потокам манере. Во время рассмотрения ключевых слов

async
и
await
языка C# будет предложен более простой подход.

В целях иллюстрации создайте приложение Windows Presentation Foundation (WPF) по имени

DataParallelismWithForEach
, выбрав шаблон WPF Арр (.NET Core). Чтобы создать проект и добавить его к решению с помощью командной строки, используйте следующие команды:

dotnet new wpf -lang c# -n DataParallelismWithForEach

– o .\DataParallelismWithForEach -f

net5.0

dotnet sln .\Chapter15_AllProjects.sln add .\DataParallelismWithForEach

На заметку! Инфраструктура Windows Presentation Foundation (WPF) в текущей версии .NET Core предназначена только для Windows и будет подробно рассматриваться в главах 24-28. Если вы еще не работали с WPF, то здесь описано все, что необходимо для данного примера. Разработка приложений WPF ведется в среде Visual Studio Code, хотя никаких визуальных конструкторов там не предусмотрено. Чтобы получить больший опыт разработки приложений WPF, рекомендуется использовать Visual Studio 2019.

Поделиться с друзьями: