В этом примере требуется дополнительный пакет 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
из указанного каталога. В настоящее время вся работа происходит в первичном потоке исполняемой программы. Следовательно, после щелчка на кнопке
Click to Flip Your Images! (Щелкните для поворота ваших изображений) программа выглядит зависшей. Вдобавок заголовок окна также сообщит о том, что файл обрабатывается тем же самым первичным потоком, т.к. в наличии есть только один поток выполнения.
Чтобы обрабатывать файлы на как можно большем количестве процессоров, текущий цикл
foreach
можно заменить вызовом метода
Parallel.ForEach()
. Вспомните, что этот метод имеет множество перегруженных версий. Простейшая форма метода принимает совместимый с
IEnumerable<T>
объект, который содержит элементы, подлежащие обработке (например, строковый массив
files
), и делегат
Action<T>
, указывающий на метод, который будет выполнять необходимую работу.