Переменные Parallel.ForEach не рассматриваются как локальные - PullRequest
0 голосов
/ 27 октября 2019

Я пишу поисковик файловой системы, который принимает каталоги (обычно диски Windows) и фильтрует их как входные, а также ищет в каждом входном каталоге файлы, соответствующие входным фильтрам. Последовательно, я реализовал это так (упрощенная версия соответствующего кода):


foreach (var dir in Directories) {
    Console.WriteLine("Searching: " + dir);
    var s = new FSSearcher(dir, FileTypes, Keywords, SearchContents);
    s.Search();
}

class FSSearcher {

    [...]
    private static List<string> Filetypes;
    private static List<string> Keywords;
    private static List<string> Results;
    private static bool searchContents;
    private static string SearchDirectory;
    [...]

    public FSSearcher(string d, List<string> f, List<string> k, bool s) {
        SearchDirectory = d;
        Filetypes = f;
        Keywords = k;
        Results = new List<string>();
        searchContents = s;
    }

    public void Search() {
            if (Directory.Exists(SearchDirectory)) {
                Console.WriteLine("Searching dir: " + SearchDirectory);
                [...]

            }
    }
}

Этот код работает правильно и как ожидалось. Никаких результатов не нужно собирать в конце цикла. FSSearcher - это «независимый класс». Теперь я хочу запустить этот цикл foreach параллельно (разные физические диски, поэтому параллелизм не должен ограничиваться вводом / выводом), поэтому я изменил приведенный выше код на следующий (снова упрощенный код):

var ConcurrentDirectories = new ConcurrentBag<string>();
ConcurrentDirectories.Add("C:\\");
ConcurrentDirectories.Add("Z:\\");
var options = new ParallelOptions { MaxDegreeOfParallelism = ConcurrentDirectories.Count };
Parallel.ForEach(ConcurrentDirectories, options, (dir) => {
        Console.WriteLine("Searching in parallel: " + dir);
        var s = new FSSearcher(dir, FileTypes, Keywords, SearchContents);
        s.Search();
});

Я предполагаю, что переменная s является локальной для каждой итерации. Однако, это не так. Приведенный выше код дает следующий вывод:

Searching in parallel: Z:\
Searching in parallel: C:\
Searching dir: C:\
Searching dir: C:\ <=== this is expected to be Z:\

Похоже, что s инициализируется для Z:\ в одной "итерации", а затем переназначается другому экземпляру FSSearcher для C:\. Затем Search() вызывается дважды для последнего назначенного объекта для C:\.

Я прочитал все соответствующие сообщения SO для объяснения или решения, но не могу найти объяснение. Как это происходит и как я могу предотвратить это?

Заранее большое спасибо!

1 Ответ

0 голосов
/ 27 октября 2019

Проблема заключалась в том, что я назначал static переменные в конструкторе FSSearch. Назначение статических переменных в конструкторе , по-видимому, не является потокобезопасным . Я изменил свой код на следующий, что привело к исчезновению проблемы параллелизма:

class FSSearcher {

    [...]
    private List<string> Filetypes;
    private List<string> Keywords;
    private List<string> Results;
    private bool searchContents;
    private string SearchDirectory;
    [...]

    public FSSearcher(string d, List<string> f, List<string> k, bool s) {
        this.SearchDirectory = d;
        this.Filetypes = f;
        this.Keywords = k;
        this.Results = new List<string>();
        this.searchContents = s;
    }

    public void Search() {
            if (Directory.Exists(SearchDirectory)) {
                Console.WriteLine("Searching dir: " + SearchDirectory);
                [...]

            }
    }
}
...