1

我创建了这个正常的 for 循环:

    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”中指定的所有依赖项。

“文件”变量应该只有唯一的元素,因为它被用作结果的键,一个字典,但我在调用方法之前检查了这个。

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;
    }

任何反馈表示赞赏

4

4 回答 4

4

假设里面的代码AnalyzeFiledependencies线程安全的,这样的事情怎么样:

var filesAnalyzed = files
    .AsParellel()
    .Select(x => new{Item = x, File = AnalyzeFile(x, dependencies)})
    .ToDictionary(x => x.Item, x=> x.File);
于 2017-08-31T14:42:00.787 回答
3

用这种方式重写你的正常循环:

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

您还应该使用ConcurrentDictionary除了 Dictionary 使所有进程线程安全

于 2017-08-31T14:41:04.127 回答
3

如果您改用 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);
});

至少在这种情况下,使用ParallelPLINQ 没有任何好处。

于 2017-08-31T14:41:33.040 回答
1

如果不调试代码,很难说到底出了什么问题。只是看着它,虽然我会使用ConcurrentDictionaryfor filesAnalyzed 变量而不是普通的 `Dictionary 并摆脱 Monitor。

我还将检查字典 filesAnalyzed 中是否已经存在相同的键,这可能是您尝试使用已添加到字典中的键添加 kvp。

于 2017-08-31T15:02:06.873 回答