c#并行编程是读取线程安全的
本文关键字:线程 安全 读取 并行 编程 | 更新日期: 2023-09-27 18:03:12
我正在读一本关于并行编程的书,它说不使用锁就向列表添加元素不是线程保存,因为结果将是不可预测的。例如,如果要向list中添加800000个元素,则最终结果将少于800000个元素。
现在我想知道是否线程保存到从列表中读取元素。例如,我有一个列表BlackListedNumbers
List<int> BlackListedNumbers = new List<int> {10, 50 ....... n};
//lets say there is 500 000 elements in the list
和另一个包含10 000 000个数字的列表Numbers
,显然我将使用parallel。为了完成这个任务,我想要的是Final
列表,其中包含Numbers
中不在BlackListedNumbers
列表中的所有数字
List<int> finalList = new List<int>();
Parallel.ForEach(Numbrs,
num =>
{
if (!blackListedNumbrs.Contains(num))
{
lock (finalList)
{
finalList.Add(num);
}
}
});
我知道这不是最有效的方法,但我只是想说明问题。
所以我的问题是:线程保存从列表blackListedNumbrs
中读取结果,我会得到100%准确的结果吗?
From MSDN:
一个
List<T>
可以支持多个reader并发,只要不修改集合。
所以如果你从不修改列表,你应该没问题。
请注意,使用HashSet<int>
会更高效——而且HashSet<T>
还支持多个读取器1。你可以也可以使用Parallel LINQ使你的查询更甜蜜,几乎肯定更有效率:
// If you want duplicates in Numbers to still come up as duplicates in the result
HashSet<int> blacklistedSet = new HashSet<int>(blackListedNumbers);
List<int> finalList = Numbers.AsParallel()
.Where(x => !blacklistedSet.Contains(x))
.ToList();
// Or if you just want a set-based operation:
List<int> finalList = Numbers.AsParallel()
.Except(blacklistedSet)
.ToList();
好多了,而且不需要锁:)
1如注释所述,我没有任何文档来支持这一点。但是从集合中读取不需要修改任何共享状态,所以至少是有意义的…
只要没有其他人在写/添加/删除blackListedNumbers
就可以
Read是安全的,所以你上面的代码应该工作得很好,事实上,用多线程向列表中添加记录会引起问题。net 4.0引入了线程安全集合在你的情况下,你可以使用ConcurrentBag使用多个线程向集合中添加项。
下面是我使用它的例子:
var data = new ConcurrentBag<DJVSStatsEv>();
Parallel.ForEach(globalData.ValuationEventsPit, item =>
{
data.Add(new DJVSStatsEv(item.DateYearMonth, item.EventType, eventGroup) {PostVal = item.PostVal, PreVal = item.PreVal, Raised = item.Raised});
});