Язык программирования C#9 и платформа .NET5
Обработка запроса на отмену
В текущий пример можно внести еще одно улучшение — предоставить пользователю способ для останова обработки данных изображений путем щелчка на второй кнопке Cancel (Отмена). К счастью, методы
иParallel.For()поддерживают отмену за счет использования маркеров отмены. При вызове методов на объектеParallel.ForEach()им можно передавать объектParallel, который в свою очередь содержит объектParallelOptions.CancellationTokenSourceПервым делом определите в производном от Window классе закрытую переменную-член
типа_cancelToken:CancellationTokenSourcepublic partial class MainWindow :Window{// Новая переменная уровня Window.private CancellationTokenSource _cancelToken =new CancellationTokenSource();...}Обновите обработчик события
:Clickprivate void cmdCancel_Click(object sender, EventArgs e){// Используется для сообщения всем рабочим потокам о необходимости останова!_cancelToken.Cancel();}Теперь можно заняться необходимыми модификациями метода
. Вот его финальная реализация:ProcessFiles()private void ProcessFiles(){<b> // Использовать экземпляр ParallelOptions для хранения CancellationToken.</b>ParallelOptions parOpts = new ParallelOptions();parOpts.CancellationToken = _cancelToken.Token;parOpts.MaxDegreeOfParallelism = System.Environment.ProcessorCount;// Загрузить все файлы *.jpg и создать новый каталог// для модифицированных данных.string[] files = Directory.GetFiles(@".\TestPictures", "*.jpg",SearchOption.AllDirectories);string outputDirectory = @".\ModifiedPictures";Directory.CreateDirectory(outputDirectory);try{// Обработать данные изображения в параллельном режиме!Parallel.ForEach(files, parOpts, currentFile =>{parOpts.CancellationToken.ThrowIfCancellationRequested();string filename = Path.GetFileName(currentFile);Dispatcher?.Invoke(() =>{this.Title =$"Processing {filename}on thread {Thread.CurrentThread.ManagedThreadId}";});using (Bitmap bitmap = new Bitmap(currentFile)){bitmap.RotateFlip(RotateFlipType.Rotate180FlipNone);bitmap.Save(Path.Combine(outputDirectory, filename));}});Dispatcher?.Invoke(()=>this.Title = "Done!");}catch (OperationCanceledException ex){Dispatcher?.Invoke(()=>this.Title = ex.Message);}}Обратите внимание, что в начале метода конфигурируется объект
с установкой его свойстваParallelOptionsдля применения маркераCancellationToken. Кроме того, этот объектCancellationTokenSourceпередается во втором параметре методуParallelOptions.Parallel.ForEach()Внутри логики цикла осуществляется вызов
на маркере отмены, гарантируя тем самым, что если пользователь щелкнет на кнопке Cancel, то все потоки будут остановлены ив качестве уведомления сгенерируется исключение времени выполнения. Перехватив исключениеThrowIfCancellationRequested(), можно добавить в текст главного окна сообщение об ошибке.OperationCanceledExceptionОбеспечение параллелизма задач с помощью класса Parallel
В дополнение к обеспечению параллелизма данных библиотека TPL также может использоваться для запуска любого количества асинхронных задач с помощью метода
. Такой подход немного проще, чем применение делегатов или типов из пространства именParallel.Invoke(), но если нужна более высокая степень контроля над выполняемыми задачами, тогда следует отказаться от использованияSystem.Threadingи напрямую работать с классомParallel.Invoke(), как делалось в предыдущем примере.TaskЧтобы взглянуть на параллелизм задач в действии, создайте новый проект консольного приложения по имени
и импортируйте в начале файлаMyEBookReaderпространства именProgram.cs,System.Threading,System.Text,System.Threading.TasksиSystem.Linq(пример является модификацией полезного примера из документации по .NET Core). Здесь мы будем извлекать публично доступную электронную книгу из сайта проекта Гутенберга (System.Net) и затем параллельно выполнять набор длительных задач. Книга загружается в методеwww.gutenberg.org, показанном ниже:GetBook()