2

我有 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;
    }
4

3 回答 3

12

不,它不是线程安全的。您可能正在寻找ConcurrentBag<T>class,一个线程安全的无序集合。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;
}
于 2013-07-10T15:54:45.220 回答
4

不,这不对。

只要集合没有被修改,一个 List 可以同时支持多个读取器。通过集合进行枚举本质上不是线程安全的过程。在枚举与一个或多个写访问竞争的极少数情况下,确保线程安全的唯一方法是在整个枚举期间锁定集合。要允许集合被多个线程访问以进行读写,您必须实现自己的同步。

来自MSDN

于 2013-07-10T15:55:23.030 回答
4

您可以通过将方法转换为 PLINQ 操作来避免线程安全问题(并提高性能):

static List<int> Calculate(List<string[]> numbers)
{
    return numbers.AsParallel()
                  .Select(nums => nums.Sum(Convert.ToInt32))
                  .OrderBy(i => i)
                  .ToList();
}
于 2013-07-10T16:11:49.837 回答