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%准确的结果吗?

c#并行编程是读取线程安全的

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