这个 List<T> 的使用是线程安全的吗?

3
我有一段C# .NET 4的代码,它在一个Parallel.For循环中向List添加内容。 我找不到确切的答案来确定这是否是线程安全的。 如果不安全的话,有什么替代方案吗?
    static List<int> Calculate(List<string[]> numbers)
    {
           List<int> sums = new List<int>();

         
            Parallel.ForEach(numbers,
            (nums) =>
            {
                int sum = 0;
                for (int i = 0; i < nums.Length; i++)
                     sum += Convert.ToInt32( nums[i]);

                // is this thread safe or not???
                sums.Add(sum);
            });

            sums.Sort();
            return sums;
    }

6
通过阅读文档,尤其是标记为“线程安全”的部分,您可以找到明确的答案。http://msdn.microsoft.com/en-us/library/6sh2ey19.aspx - Eric Lippert
这个回答解决了你的问题吗?线程安全的List<T>属性 - Michael Freidgeim
3个回答

12

不,它不是线程安全的。你可能正在寻找ConcurrentBag<T>,它是一个线程安全的无序集合。有关更多信息和其他线程安全集合,请参阅MSDN 的线程安全集合文档。例如:

static List<int> Calculate(List<string[]> numbers)
{
       var sums = new ConcurrentBag<int>();


        Parallel.ForEach(numbers,
        (nums) =>
        {
            int sum = 0;
            for (int i = 0; i < nums.Length; i++)
                 sum += Convert.ToInt32( nums[i]);

            sums.Add(sum);
        });

        var sorted = sums.OrderBy(x => x).ToList();
        return sorted;
}

1
我删除了我的评论和回答,因为我认为sumsnums是同一个列表。事实并非如此,这使得我所有的论证都无效了。您当然是正确的,并发包在这里确实有帮助。 - Daniel Hilgarth

4

通过将方法转换为PLINQ操作,您可以避免线程安全问题(并获得更好的性能):

static List<int> Calculate(List<string[]> numbers)
{
    return numbers.AsParallel()
                  .Select(nums => nums.Sum(Convert.ToInt32))
                  .OrderBy(i => i)
                  .ToList();
}

3

不,不是。

只要不修改集合,列表就可以同时支持多个读取器。枚举集合本质上不是线程安全的过程。在罕见情况下,当一个枚举与一个或多个写访问发生冲突时,确保线程安全的唯一方法是在整个枚举期间锁定集合。为了允许多个线程对集合进行读写访问,必须实现自己的同步。

来自MSDN


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接