我正在读一本有关并行编程的书,它说在不使用锁的情况下将元素添加到列表中并不是线程节省,因为结果将不可预测。例如,如果我们必须添加80万个元素,那么最终结果将少于80万个元素。

现在我想知道从列表中读取元素是否是线程保存。例如,假设我有一个列表BlackListedNumbers

List<int> BlackListedNumbers = new List<int> {10, 50 ....... n};
//lets say there is 500 000 elements in the list


还有一个包含10000000数字的列表Numbers,显然我将使用parallel.Foreach完成此任务,我想要的是Final列表,其中包含Numbers列表中所有不在BlackListedNumbers列表中的数字

List<int> finalList = new List<int>();
Parallel.ForEach(Numbrs,
  num =>
    {
      if (!blackListedNumbrs.Contains(num))
      {
        lock (finalList)
        {
          finalList.Add(num);
        }
      }
  });


我知道这不是完成此任务的最有效方法,但我只是想说明问题。

所以我的问题是:从列表blackListedNumbrs读取结果是否保存线程,我将获得100%准确的结果吗?

最佳答案

MSDN


  只要不修改集合,List<T>就可以同时支持多个阅读器。


因此,如果您从不修改列表,就可以了。

请注意,使用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如评论中所述,我没有任何文档来支持此操作。但是从集合中读取并不需要修改任何共享状态,因此至少有意义。

10-05 18:29
查看更多