Параллельное программирование на С#
Мне интересно узнать о параллельном программировании на С#.NET(не так, как все, что нужно знать, но основы и, возможно, некоторые хорошие практики), поэтому я решил перепрограммировать старую мою программу, которая называемый ImageSyncer. ImageSyncer - действительно простая программа, все, что она делает - это сканировать через папку и находить все файлы, заканчивающиеся на .jpg, затем вычисляет новую позицию файлов в зависимости от даты, когда они были взяты (синтаксический анализ xif-данных или любого другого он назывался). После того, как местоположение было сгенерировано, программа проверяет наличие каких-либо существующих файлов в этом месте, и, если таковая существует, она смотрит на последнее время записи как файла для копирования, так и файла "на своем пути". Если они равны, файл пропускается. Если не создается контрольная сумма md5 обоих файлов и сопоставляется. Если нет совпадения, файл, подлежащий копированию, получает новое место для копирования (например, если оно должно быть скопировано в "C:\test.jpg", оно копируется в "C:\test (1)". jpg "вместо этого). Результат этой операции заполняется в очередь struct-type, которая содержит две строки, исходный файл и позицию для его копирования. Затем эта очередь повторяется до тех пор, пока она не будет пустой и файлы будут скопированы.
Другими словами, существует 4 операции:
1. Scan directory for jpegs
2. Parse files for xif and generate copy-location
3. Check for file existence and if needed generate new path
4. Copy files
И поэтому я хочу переписать эту программу, чтобы сделать ее paralell и иметь возможность выполнять несколько операций одновременно, и мне было интересно, какой лучший способ достичь этого будет. Я придумал две разные модели, о которых я могу думать, но ни один из них не может быть вообще хорош. Первый из них состоит в том, чтобы распараллелить 4 этапа старой программы, так что, когда первый шаг должен быть выполнен, он выполняется по нескольким потокам, и когда завершается весь этап 1, начинается этап 2. Другой (который я считаю более интересным, потому что я понятия не имею, как это сделать) заключается в создании своего рода рабочей и потребительской модели, поэтому, когда поток завершается с шагом 1, другой берет на себя и выполняет второй шаг при этом объект (или что-то в этом роде). Но, как сказано, я не знаю, есть ли какие-либо из них хорошие решения. Кроме того, я мало что знаю о параллельном программировании. Я знаю, как сделать поток, и как заставить его выполнять функцию, принимающую объект как единственный параметр, и я также использовал класс BackgroundWorker один раз, но я не знаком с кем-либо из них.
Любой ввод будет оценен.
Ответы
Ответ 1
Есть несколько вариантов:
[Но как @John Knoeller указал, пример, который вы дали, скорее всего, будет последовательным ограничением ввода/вывода]
Ответ 2
Это ссылка, которую я использую для потока С#: http://www.albahari.com/threading/
Как один PDF: http://www.albahari.com/threading/threading.pdf
Для вашего второго подхода:
Я работал над некоторыми многопоточными приложениями-производителями/потребителями, где каждая задача - это какой-то код, который навсегда зацикливается. Внешний "инициализатор" запускает отдельный поток для каждой задачи и инициализирует EventWaitHandle для каждой задачи. Для каждой задачи есть глобальная очередь, которая может использоваться для создания/потребления ввода.
В вашем случае ваша внешняя программа добавит каждый каталог в очередь для Task1 и задает EventWaitHandler для Task1. Задача 1 "проснется" из своего EventWaitHandler, получит количество каталогов в своей очереди, а затем, когда число больше 0, получите каталог из очереди, сканируйте все .jpg и добавьте каждое .jpg место во вторую очередь, и установите задачу EventWaitHandle для задачи 2. Задача 2 считывает свой вход, обрабатывает ее, пересылает ее в очередь для задачи 3...
Это может быть немного больно, когда все блокировки работают правильно (я в основном блокирую любой доступ к очереди, даже что-то простое, как получение его счета). Предполагается, что .NET 4.0 имеет структуры данных, которые автоматически поддерживают очередь производителей/потребителей без блокировок.
Ответ 3
Интересная проблема.
Я придумал два подхода. Первая основана на PLinq, а вторая основана на te Rx Framework.
Первый повторяется через файлы параллельно.
Второй генерирует асинхронно файлы из каталога.
Вот как это выглядит в упрощенной версии (первый метод требует .Net 4.0, поскольку он использует PLinq)
string direcory = "Mydirectory";
var jpegFiles = System.IO.Directory.EnumerateFiles(direcory,"*.jpg");
// -- PLinq --------------------------------------------
jpegFiles
.AsParallel()
.Select(imageFile => new {OldLocation = imageFile, NewLocation = GenerateCopyLocation(imageFile) })
.Do(fileInfo =>
{
if (!File.Exists(fileInfo.NewLocation ) ||
(File.GetCreationTime(fileInfo.NewLocation)) != (File.GetCreationTime(fileInfo.NewLocation)))
File.Copy(fileInfo.OldLocation,fileInfo.NewLocation);
})
.Run();
// -----------------------------------------------------
//-- Rx Framework ---------------------------------------------
var resetEvent = new AutoResetEvent(false);
var doTheWork =
jpegFiles.ToObservable()
.Select(imageFile => new {OldLocation = imageFile, NewLocation = GenerateCopyLocation(imageFile) })
.Subscribe( fileInfo =>
{
if (!File.Exists(fileInfo.NewLocation ) ||
(File.GetCreationTime(fileInfo.NewLocation)) != (File.GetCreationTime(fileInfo.NewLocation)))
File.Copy(fileInfo.OldLocation,fileInfo.NewLocation);
},() => resetEvent.Set());
resetEvent.WaitOne();
doTheWork.Dispose();
// -----------------------------------------------------