Параллельное программирование в C # - PullRequest
13 голосов
/ 15 февраля 2010

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

И поэтому я хочу переписать эту программу, чтобы она стала параллельной и могла выполнять несколько операций одновременно, и мне было интересно, каким будет лучший способ добиться этого. Я придумал две разные модели, о которых могу только подумать, но ни одна из них не может быть хорошей. Первый - распараллелить 4 шага старой программы, чтобы при выполнении первого шага он выполнялся в нескольких потоках, а когда весь шаг 1 завершен, начинается шаг 2. Другой (который я нахожу более интересным, потому что я понятия не имею, как это сделать) - создать своего рода рабочую и потребительскую модель, поэтому, когда поток завершается с шагом 1, другой переходит и выполняет шаг 2 при этом. объект (или что-то подобное). Но, как я уже сказал, я не знаю, является ли какое-либо из них хорошим решением. Кроме того, я совсем немного знаю о параллельном программировании. Я знаю, как создать поток, и как заставить его выполнять функцию, принимающую объект в качестве единственного параметра, и я также однажды использовал класс BackgroundWorker, но я не очень знаком с любым из них. .

Любой вклад будет оценен.

Ответы [ 3 ]

6 голосов
/ 15 февраля 2010

Есть несколько вариантов:

[Но, как указал @Джон Кноеллер, приведенный вами пример, вероятно, будет связан с последовательным вводом / выводом]

2 голосов
/ 15 февраля 2010

Это ссылка, которую я использую для потока C #: http://www.albahari.com/threading/

Как один PDF: http://www.albahari.com/threading/threading.pdf

Для вашего второго подхода:

Я работал над некоторыми многопоточными приложениями производителя / потребителя, где каждая задача представляет собой некоторый код, который зацикливается навсегда. Внешний «инициализатор» запускает отдельный поток для каждой задачи и инициализирует EventWaitHandle для каждой задачи. Для каждой задачи есть глобальная очередь, которую можно использовать для ввода / вывода.

В вашем случае ваша внешняя программа добавит каждый каталог в очередь для Task1 и установит EventWaitHandler для Task1. Задача 1 «проснется» из своего EventWaitHandler, получит количество каталогов в своей очереди, а затем, когда число превысит 0, получит каталог из очереди, просканирует все файлы .jpgs и добавит каждое местоположение .jpg. во вторую очередь и установите EventWaitHandle для задачи 2. Задача 2 считывает входные данные, обрабатывает их, пересылает в очередь для Задачи 3 ...

Может быть немного трудно заставить всю блокировку работать правильно (я в основном блокирую любой доступ к очереди, даже такой простой, как получение ее счетчика). Предполагается, что в .NET 4.0 имеются структуры данных, которые будут автоматически поддерживать очередь производителя / потребителя без блокировок.

1 голос
/ 15 февраля 2010

Интересная проблема. Я придумал два подхода. Первый основан на PLinq, а второй - на 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();

    // -----------------------------------------------------
...