我的问题很少理论我想知道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
列表中的正确数据是由于多个线程而改变,因为我使用了正常的循环?
注意:我不担心列表顺序。我想知道列表和数据的长度。
答案 0 :(得分:7)
此解决方案是否是线程安全的?技术上是的,实际上没有。
线程安全List
(与队列或包相对)的概念是该列表对于 order 是安全的,或者更严格地说,< em> index ,因为除了升序整数之外没有其他键。在一个平行的世界中,当你想到它时,它就是一种荒谬的概念。这就是System.Collections.Concurrent命名空间包含ConcurrentBag
和ConcurrentQueue
但不包含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
ConcurrentBag的所有公共成员和受保护成员都是线程安全的,可以从多个线程同时使用。
答案 2 :(得分:2)
List<T>
不是线程安全的。对于您使用它的方式,不需要线程安全。从多个线程同时访问资源时,您需要线程安全。你没有这样做,因为你正在研究本地名单。
最后,您将本地列表的内容添加到result
变量中。由于您使用lock
进行此操作,因此您在该块中是线程安全的。
所以你的解决方案可能很好。