对于不处理所有元素,这是如何并行的?
How is this parallel for not processing all elements?
我已经创建了这个正常的 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" 中指定的所有依赖项。
"files" 变量应该只包含唯一元素,因为它被用作结果的键,一个字典,但我在调用方法之前检查了这一点。
for 循环工作正常,所有元素都在单线程中处理,所以我想通过更改为并行 for 循环来提高性能,问题是并非所有元素都来自 "files" 变量正在并行处理(在我的测试用例中,我得到 30 个元素而不是 53 个)。
我尝试增加时间跨度,或者删除所有 "Monitor.TryEnter" 代码并仅使用一个锁 (filesAnalyzed),但仍然得到相同的结果
我对 paraller for 不是很熟悉,所以它可能与我使用的语法有关。
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;
}
欢迎任何反馈
以这种方式重写您的正常循环:
Parallel.Foreach(files, item=>
{
filesAnalyzed[item] = AnalyzeFile(item, dependencies);
});
您还应该使用 ConcurrentDictionary 除了 Dictionary 以使所有进程线程安全
如果改用并行 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 没有任何好处。
假设 AnalyzeFile
和 dependencies
中的代码是线程安全的,那么这样的代码怎么样:
var filesAnalyzed = files
.AsParellel()
.Select(x => new{Item = x, File = AnalyzeFile(x, dependencies)})
.ToDictionary(x => x.Item, x=> x.File);
如果不调试代码,很难说到底出了什么问题。只是看看它,尽管我会为 filesAnalyzed 变量使用 ConcurrentDictionary
而不是普通的 `Dictionary 并摆脱监视器。
我还会检查字典文件中是否已存在相同的键已分析,可能是您正在尝试使用已添加到字典中的键添加 kvp。
我已经创建了这个正常的 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" 中指定的所有依赖项。
"files" 变量应该只包含唯一元素,因为它被用作结果的键,一个字典,但我在调用方法之前检查了这一点。
for 循环工作正常,所有元素都在单线程中处理,所以我想通过更改为并行 for 循环来提高性能,问题是并非所有元素都来自 "files" 变量正在并行处理(在我的测试用例中,我得到 30 个元素而不是 53 个)。
我尝试增加时间跨度,或者删除所有 "Monitor.TryEnter" 代码并仅使用一个锁 (filesAnalyzed),但仍然得到相同的结果
我对 paraller for 不是很熟悉,所以它可能与我使用的语法有关。
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;
}
欢迎任何反馈
以这种方式重写您的正常循环:
Parallel.Foreach(files, item=>
{
filesAnalyzed[item] = AnalyzeFile(item, dependencies);
});
您还应该使用 ConcurrentDictionary 除了 Dictionary 以使所有进程线程安全
如果改用并行 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 没有任何好处。
假设 AnalyzeFile
和 dependencies
中的代码是线程安全的,那么这样的代码怎么样:
var filesAnalyzed = files
.AsParellel()
.Select(x => new{Item = x, File = AnalyzeFile(x, dependencies)})
.ToDictionary(x => x.Item, x=> x.File);
如果不调试代码,很难说到底出了什么问题。只是看看它,尽管我会为 filesAnalyzed 变量使用 ConcurrentDictionary
而不是普通的 `Dictionary 并摆脱监视器。
我还会检查字典文件中是否已存在相同的键已分析,可能是您正在尝试使用已添加到字典中的键添加 kvp。