Параллельное программирование на С#

Мне интересно узнать о параллельном программировании на С#.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();

    // -----------------------------------------------------