Я создал это нормально для цикла:

    public static Dictionary<string,Dictionary<string,bool>> AnalyzeFiles(IEnumerable<string> files, IEnumerable<string> dependencies)
    {
        Dictionary<string, Dictionary<string, bool>> filesAnalyzed = new Dictionary<string, Dictionary<string, bool>>();
        foreach (var item in files)
        {
            filesAnalyzed[item] = AnalyzeFile(item, dependencies);
        }
        return filesAnalyzed;
    }

Цикл просто проверяет, имеет ли каждый файл, который находится в переменной «files», все зависимости, указанные в переменной «dependencies».

Переменная "files" должна иметь только уникальные элементы, потому что она используется в качестве ключа для результата, словаря, но я проверяю это перед вызовом метода.

Цикл for работает правильно, и все элементы обрабатываются в одном потоке, поэтому я хотел повысить производительность, переключившись на параллельный цикл for, проблема в том, что не все элементы, которые происходят из переменной «files», обрабатываются в параллельно для (в моем тестовом случае я получаю 30 элементов вместо 53).

Я пытался увеличить временной интервал или удалить весь код «Monitor.TryEnter» и использовать только блокировку (filesAnalyzed), но все равно получил тот же результат

Я не очень знаком с параллелью для, так что это может быть что-то в синтаксисе, который я использую.

    public static Dictionary<string,Dictionary<string,bool>> AnalyzeFiles(IEnumerable<string> files, IEnumerable<string> dependencies)
    {
        var filesAnalyzed = new Dictionary<string, Dictionary<string, bool>>();

        Parallel.For<KeyValuePair<string, Dictionary<string, bool>>>(
            //start index
            0,
            //end index
            files.Count(),
            // initialization?
            ()=>new KeyValuePair<string, Dictionary<string, bool>>(),
            (index, loop, result) =>
            {
                var temp = new KeyValuePair<string, Dictionary<string, bool>>(
                               files.ElementAt(index),
                               AnalyzeFile(files.ElementAt(index), dependencies));
                return temp;
            }
            ,
            //finally
            (x) =>
            {
                if (Monitor.TryEnter(filesAnalyzed, new TimeSpan(0, 0, 30)))
                {
                    try
                    {
                        filesAnalyzed.Add(x.Key, x.Value);
                    }
                    finally
                    {
                        Monitor.Exit(filesAnalyzed);
                    }
                }
            }
            );
        return filesAnalyzed;
    }

Любая обратная связь приветствуется

1
Robson 31 Авг 2017 в 17:29

4 ответа

Лучший ответ

Перепишите свой обычный цикл следующим образом:

   Parallel.Foreach(files, item=>
    {
        filesAnalyzed[item] = AnalyzeFile(item, dependencies);
    });

Вам также следует использовать ConcurrentDictionary , кроме словаря, чтобы сделать весь процесс потокобезопасным

3
MikkaRin 31 Авг 2017 в 14:43

Трудно сказать, что именно идет не так, без отладки кода. Просто глядя на это, я бы использовал ConcurrentDictionary для переменной filesAnalyzed вместо обычного `Dictionary и избавился бы от Monitor.

Я бы также проверил, существует ли такой же ключ в файлах словаря. Анализируется, возможно, вы пытаетесь добавить kvp с ключом, который уже добавлен в словарь.

1
Dogu Arslan 31 Авг 2017 в 15:02

Вы можете упростить свой код на много , если вместо этого используете Parallel LINQ:

public static Dictionary<string,Dictionary<string,bool>> AnalyzeFiles(IEnumerable<string> files, IEnumerable<string> dependencies)
{
    var filesAnalyzed = ( from item in files.AsParallel()
                          let result=AnalyzeFile(item, dependencies)
                          select (Item:item,Result:result)
                        ).ToDictionary( it=>it.Item,it=>it.Result)               
    return filesAnalyzed;
}

В этом случае я использовал синтаксис кортежа, чтобы избежать шума. Это также сокращает ассигнования.

Используя синтаксис метода, можно записать то же самое, что и:

var filesAnalyzed = files.AsParallel()
                         .Select(item=> (item, AnalyzeFile(item, dependencies)))
                         .ToDictionary( it=>it.Item,it=>it.Result)               

Dictionary<> не является поточно-ориентированным для модификации. Если вы хотите использовать Parallel.ForEach без блокировки, вам придется использовать ConcurrentDictionary

var filesAnalyzed = ConcurrentDictionary<string,Dictionary<string,bool>>;

Parallel.ForEach(files,file => {
    filesAnalyzed[item] = AnalyzeFile(item, dependencies);
});

В этом случае, по крайней мере, нет никакого преимущества в использовании Parallel по сравнению с PLINQ.

3
Panagiotis Kanavos 31 Авг 2017 в 14:48

Предполагая, что код внутри AnalyzeFile и dependencies является потокобезопасным, как насчет чего-то вроде этого:

var filesAnalyzed = files
    .AsParellel()
    .Select(x => new{Item = x, File = AnalyzeFile(x, dependencies)})
    .ToDictionary(x => x.Item, x=> x.File);
4
Magnus 31 Авг 2017 в 14:52