Рейтинговые книги
Читем онлайн Язык программирования C#9 и платформа .NET5 - Эндрю Троелсен

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 186 187 188 189 190 191 192 193 194 ... 407
обеспечением параллелизма данных. Таким термином обозначается задача прохода по массиву или коллекции в параллельной манере с помощью метода 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.

Дважды щелкните на имени файла MainWindow.xaml в окне Solution Explorer и поместите в него показанное далее содержимое XAML:

<Window x:Class="DataParallelismWithForEach.MainWindow"

    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"

    xmlns:local="clr-namespace:DataParallelismWithForEach"

    mc:Ignorable="d"

    Title="Fun with TPL" Height="400" Width="800">

  <Grid>

    <Grid.RowDefinitions>

      <RowDefinition Height="Auto"/>

      <RowDefinition Height="*"/>

      <RowDefinition Height="Auto"/>

    </Grid.RowDefinitions>

    <Label Grid.Row="0" Grid.Column="0">

      Feel free to type here while the images are processed...

    </Label>

    <TextBox Grid.Row="1" Grid.Column="0"  Margin="10,10,10,10"/>

    <Grid Grid.Row="2" Grid.Column="0">

      <Grid.ColumnDefinitions>

        <ColumnDefinition Width="Auto"/>

        <ColumnDefinition Width="*"/>

        <ColumnDefinition Width="Auto"/>

      </Grid.ColumnDefinitions>

      <Button Name="cmdCancel" Grid.Row="0" Grid.Column="0"

              Margin="10,10,0,10"

              Click="cmdCancel_Click">

        Cancel

      </Button>

      <Button Name="cmdProcess" Grid.Row="0" Grid.Column="2"

              Margin="0,10,10,10"

              Click="cmdProcess_Click">

              Click to Flip Your Images!

      </Button>

    </Grid>

  </Grid>

</Window>

И снова пока не следует задаваться вопросом о том, что означает приведенная разметка или как она работает; вскоре вам придется посвятить немало времени на исследование WPF. Графический пользовательский интерфейс приложения состоит из многострочной текстовой области TextBox и одной кнопки Button (по имени cmdProcess). Текстовая область предназначена для ввода данных во время выполнения работы в фоновом режиме, иллюстрируя тем самым неблокирующую природу параллельной задачи.

В этом примере требуется дополнительный пакет NuGet (System.Drawing.Common). Чтобы добавить его в проект, введите следующую команду (целиком в одной строке) в окне командной строки (в каталоге, где находится файл решения) или в консоли диспетчера пакетов в Visual Studio:

dotnet add DataParallelismWithForEach package System.Drawing.Common

Дважды щелкнув на имени файла MainWindow.xaml.cs (может потребоваться развернуть узел MainWindow.xaml), добавьте в его начало представленные ниже операторы using:

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

// (System.Threading.Tasks уже должно присутствовать благодаря

// выбранному шаблону.)

using System;

using System.Drawing;

using System.Threading.Tasks;

using System.Threading;

using System.Windows;

using System.IO;

На заметку! Вы должны обновить строку, передаваемую методу Directory.GetFiles(), чтобы в ней был указан конкретный путь к каталогу на вашей машине, который содержит файлы изображений. Для вашего удобства в каталог TestPictures включено несколько примеров изображений (поставляемых в составе операционной системы Windows).

public partial class MainWindow : Window

{

  public MainWindow()

  {

    InitializeComponent();

  }

  private void cmdCancel_Click(object sender, EventArgs e)

  {

    // Код метода будет вскоре обновлен.

  }

  private void cmdProcess_Click(object sender, EventArgs e)

  {

    ProcessFiles();

    this.Title = "Processing Complete";

  }

  private void ProcessFiles()

  {

    // Загрузить все файлы *.jpg и создать новый каталог

    // для модифицированных данных.

    // Получить путь к каталогу с исполняемым файлом.

    // В режиме отладки VS 2019 текущим каталогом будет

    // <каталог npoeктa>bindebugnet5.0 - windows.

    // В случае VS Code или команды dotnet run текущим

    // каталогом будет <каталог проекта>.

    var basePath = Directory.GetCurrentDirectory();

    var pictureDirectory =

      Path.Combine(basePath, "TestPictures");

    var outputDirectory =

      Path.Combine(basePath, "ModifiedPictures");

    // Удались любые существующие файлы.

    if (Directory.Exists(outputDirectory))

    {

      Directory.Delete(outputDirectory, true);

    }

    Directory.CreateDirectory(outputDirectory);

    string[] files = Directory.GetFiles(pictureDirectory,

       "*.jpg", SearchOption.AllDirectories);

    // Обработать данные изображений в блокирующей манере.

    foreach (string currentFile in files)

    {

      string filename =

        System.IO.Path.GetFileName(currentFile);

      // Вывести идентификатор потока, обрабатывающего текущее изображение.

      this.Title = $"Processing {filename}

        on thread {Thread.CurrentThread.ManagedThreadId}";

      using (Bitmap bitmap = new Bitmap(currentFile))

      {

        bitmap.RotateFlip(RotateFlipType.Rotate180FlipNone);

        bitmap.Save(System.IO.Path.Combine(

          outputDirectory, filename));

      }

    }

  }

}

На заметку! В случае получения сообщения об ошибке, связанной с неоднозначностью имени Path между System.IO.Path и System.Windows.Shapes.Path, либо удалите оператор using для System.Windows.Shapes, либо добавьте System.IO к Path: System.IO.Path.Combine(...).

Обратите внимание, что метод ProcessFiles() выполнит поворот изображения в каждом файле *.jpg из

1 ... 186 187 188 189 190 191 192 193 194 ... 407
На этой странице вы можете бесплатно читать книгу Язык программирования C#9 и платформа .NET5 - Эндрю Троелсен бесплатно.

Оставить комментарий