我有一个方法,它读取一个文本文件,每行包含一个int值,为了使读取更快,我使用Parallel.ForEach
,但我所看到的行为是意外的,我在文件中有800行但是当我运行这个方法时,每次它返回不同的HashSet计数时,我在搜索后读到的是Parallel.ForEach
生成多个线程并且当所有线程完成其工作时它返回结果,但是我的代码执行矛盾,或者我错过了一些重要的东西?
这是我的方法:
private HashSet<int> GetKeyItemsProcessed()
{
HashSet<int> keyItems = new HashSet<int>();
if (!File.Exists(TrackingFilePath))
return keyItems;
// normal foreach works fine
//foreach(var keyItem in File.ReadAllLines(TrackingFilePath))
//{
// keyItems.Add(int.Parse(keyItem));
//}
// this does not return right number of hashset rows
Parallel.ForEach(File.ReadAllLines(TrackingFilePath).AsParallel(), keyItem =>
{
keyItems.Add(int.Parse(keyItem));
});
return keyItems;
}
答案 0 :(得分:5)
HashSet.Add
不是线程安全的。
来自MSDN:
此类型的任何公共静态(在Visual Basic中为Shared)成员都是 线程安全。任何实例成员都不能保证是线程 安全
多线程时序的不可预测性可能并且似乎正在引发问题。
您可以将访问包装在同步构造中,这有时比并发集合更快,但在某些情况下可能无法加快速度。正如其他人所提到的,另一种选择是使用线程安全集合,如ConcurrenDictionary
或ConcurrentQueue
,尽管这些可能会产生额外的内存开销。
请务必根据时间对任何结果进行基准测试。单线程访问的原始功能有时比处理线程开销更快。编写此代码可能不值得。
最后一句话是,HashSet
单独,没有同步,对多线程操作来说是不可接受的。