C#并行循环局部变量线程安全信息

时间:2017-01-05 11:01:29

标签: c# asp.net .net multithreading task-parallel-library

我的问题很少理论我想知道List<object>是否是线程安全的,如果我以这种方式使用Parallel.For。请参阅以下内容:

public static List<uint> AllPrimesParallelAggregated(uint from, uint to)
        {
            List<uint> result = new List<uint>();
            Parallel.For((int)from, (int)to,
                () => new List<uint>(), // Local state initializer
                (i, pls, local) =>      // Loop body
                {
                    if (IsPrime((uint)i))
                    {
                        local.Add((uint)i);
                    }
                    return local;
                },
                local =>                // Local to global state combiner
                {
                    lock (result)
                    {
                        result.AddRange(local);
                    }
                });
            return result;
        }

local列表是否是线程安全的?我是否在没有数据的result列表中的正确数据是由于多个线程而改变,因为我使用了正常的循环?

注意:我不担心列表顺序。我想知道列表和数据的长度。

3 个答案:

答案 0 :(得分:7)

此解决方案是否是线程安全的?技术上是的,实际上没有。

线程安全List(与队列或包相对)的概念是该列表对于 order 是安全的,或者更严格地说,< em> index ,因为除了升序整数之外没有其他键。在一个平行的世界中,当你想到它时,它就是一种荒谬的概念。这就是System.Collections.Concurrent命名空间包含ConcurrentBagConcurrentQueue但不包含ConcurrentList的原因。

由于您询问列表的线程安全性,我假设您的软件要求生成一个按升序排列的列表。如果是这种情况,不,您的解决方案将无效。虽然代码在技术上是线程安全的,但线程可以按任何顺序完成,而变量result最终将无法排序。

如果您希望使用并行计算,则必须将结果存储在一个包中,然后在所有线程完成后对包进行排序以生成有序列表。否则,您必须按顺序执行计算。

既然你必须使用一个包,你也可以使用ConcurrentBag,然后你不必费心使用lock{}声明。

答案 1 :(得分:3)

列表不是线程安全的。但您当前的算法有效。正如其他答案和评论中所解释的那样。

这是关于For.Parallel的localInit的描述

  

<paramref name="localInit"/>委托为参与循环执行的每个线程调用一次,并为每个线程返回初始本地状态。这些初始状态将传递给第一个

IMO您在循环中添加了不必要的复杂性。我会改用ConcurrentBag。这是设计线程安全的。

ConcurrentBag<uint> result = new ConcurrentBag<uint>();
Parallel.For((long) from, (long) to,
    (i, PLS) =>
    {
        if (IsPrime((uint)i))
        {
            result.Add((uint)i); // this is thread safe. don't worry
        }
    });
return result.OrderBy(I => I).ToList(); // order if that matters

See concurrent bag here

  

ConcurrentBag的所有公共成员和受保护成员都是线程安全的,可以从多个线程同时使用。

答案 2 :(得分:2)

List<T>不是线程安全的。对于您使用它的方式,不需要线程安全。从多个线程同时访问资源时,您需要线程安全。你没有这样做,因为你正在研究本地名单。

最后,您将本地列表的内容添加到result变量中。由于您使用lock进行此操作,因此您在该块中是线程安全的。

所以你的解决方案可能很好。