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

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 193 194 195 196 197 198 199 200 201 ... 407
второго параметра выходит

      // за пределы допустимого диапазона.

   Console.WriteLine("Something bad happened");

    });

  }

}

Отмена операций async/await

Шаблон async/await также допускает отмену, которая реализуется намного проще, чем с методом Parallel.ForEach(). Для демонстрации будет применяться тот же самый проект приложения WPF, рассмотренный ранее в главе. Вы можете либо повторно использовать этот проект, либо создать в решении новый проект приложения WPF (.NET Core) и добавить к нему пакет System.Drawing.Common с помощью следующих команд CLI:

dotnet new wpf -lang c# -n PictureHandlerWithAsyncAwait

               -o .PictureHandlerWithAsyncAwait -f net5.0

dotnet sln .Chapter15_AllProjects.sln add .PictureHandlerWithAsyncAwait

dotnet add PictureHandlerWithAsyncAwait package System.Drawing.Common

Если вы работаете в Visual Studio, тогда щелкните правой кнопкой мыши на имени решения в окне Solution Explorer, выберите в контекстном меню пункт AddProject (Добавить►Проект) и назначьте ему имя PictureHandlerWithAsyncAwait. Сделайте новый проект стартовым, щелкнув правой кнопкой мыши на его имени и выбрав в контекстном меню пункт Set as Startup Project (Установить как стартовый проект). Добавьте NuGet-пакет System.Drawing.Common:

dotnet add PictureHandlerWithAsyncAwait package System.Drawing.Common

Приведите разметку XAML в соответствие с предыдущим проектом приложения WPF, но с заголовком Picture Handler with Async/Await.

Удостоверьтесь, что в файле MainWindow.xaml.cs присутствуют показанные ниже операторы using:

using System;

using System.IO;

using System.Threading;

using System.Threading.Tasks;

using System.Windows;

using System.Drawing;

Затем добавьте переменную уровня класса для объекта CancellationToken и обработчик событий для кнопки Cancel:

private CancellationTokenSource _cancelToken = null;

private void cmdCancel_Click(object sender, EventArgs e)

{

  _cancelToken.Cancel();

}

Процесс здесь такой же, как в предыдущем примере: получение каталога с файлами изображений, создание выходного каталога, получение файлов, поворот изображений в файлах и сохранение их в выходном каталоге. В новой версии для выполнения работы будут применяться асинхронные методы, а не Parallel.ForEach(), и сигнатуры методов принимают в качестве параметра объект CancellationToken. Введите следующий код:

private async void cmdProcess_Click(object sender, EventArgs e)

{

  _cancelToken = new CancellationTokenSource();

  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);

  try

  {

    foreach(string file in files)

    {

      try

      {

        await ProcessFile(

         file, outputDirectory,_cancelToken.Token);

      }

      catch (OperationCanceledException ex)

      {

        Console.WriteLine(ex);

        throw;

      }

    }

  }

  catch (OperationCanceledException ex)

  {

    Console.WriteLine(ex);

    throw;

  }

  catch (Exception ex)

  {

    Console.WriteLine(ex);

    throw;

  }

  _cancelToken = null;

  this.Title = "Processing complete";

}

После начальных настроек в коде организуется цикл по файлам с асинхронным вызовом метода ProcessFile() для каждого файла. Вызов метода ProcessFile() помещен внутрь блока try/catch и ему передается объект CancellationToken. Если вызов Cancel() выполняется на CancellationTokenSource (т.е. когда пользователь щелкает на кнопке Cancel), тогда генерируется исключение OperationCanceledException.

На заметку! Код try/catch может находиться где угодно в цепочке вызовов (как вскоре вы увидите). Размещать его при первом вызове или внутри самого асинхронного метода — вопрос личных предпочтений и нужд приложения.

Наконец, добавьте финальный метод ProcessFile():

private async Task ProcessFile(string currentFile,

  string outputDirectory, CancellationToken token)

{

  string filename = Path.GetFileName(currentFile);

  using (Bitmap bitmap = new Bitmap(currentFile))

  {

    try

    {

      await Task.Run(() =>

      {

        Dispatcher?.Invoke(() =>

        {

          this.Title = $"Processing {filename}";

        });

        bitmap.RotateFlip(RotateFlipType.Rotate180FlipNone);

        bitmap.Save(Path.Combine(outputDirectory, filename));

      }

     ,token);

    }

    catch (OperationCanceledException ex)

    {

      Console.WriteLine(ex);

      throw;

    }

  }

}

Метод ProcessFile() использует еще одну перегруженную версию Task.Run(), которая принимает в качестве параметра объект CancellationToken. Вызов Task.Run() помещен внутрь блока try/catch (как и вызывающий код) на случай щелчка пользователем на кнопке Cancel.

Асинхронные потоки (нововведение в версии 8.0)

В версии C# 8.0 появилась возможность создания и потребления потоков данных (раскрываются в главе 20) асинхронным образом. Метод, который возвращает асинхронный поток данных:

• объявляется с модификатором async;

• возвращает реализацию IAsyncEnumerable<T>;

• содержит операторы yield return (рассматривались в главе 8) для возвращения последовательных элементов в асинхронном потоке данных.

Взгляните на приведенный далее пример:

public static async IAsyncEnumerable<int> GenerateSequence()

{

  for (int i = 0; i < 20; i++)

  {

   await Task.Delay(100);

    yield return i;

  }

}

Метод GenerateSequence() объявлен как async, возвращает реализацию IAsyncEnumerable<int> и применяет yield return для возвращения целых чисел из последовательности. Чтобы вызывать этот метод, добавьте следующий код:

await foreach (var number in GenerateSequence())

{

  Console.WriteLine(number);

}

Итоговые сведения о ключевых словах async и await

Настоящий раздел содержал много примеров; ниже перечислены ключевые моменты, которые в нем рассматривались.

• Методы (а также лямбда-выражения или анонимные методы) могут быть помечены ключевым словом async, что позволяет им работать в неблокирующей манере.

• Методы (а также лямбда-выражения или анонимные методы), помеченные ключевым словом async, будут выполняться синхронно до тех пор, пока не встретится ключевое слово await.

• Один метод async может иметь множество контекстов await.

• Когда встречается выражение await, вызывающий поток приостанавливается до тех пор, пока ожидаемая задача не завершится. Тем временем управление возвращается коду, вызвавшему метод.

• Ключевое

1 ... 193 194 195 196 197 198 199 200 201 ... 407
На этой странице вы можете бесплатно читать книгу Язык программирования C#9 и платформа .NET5 - Эндрю Троелсен бесплатно.

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