为什么以下代码的结果(list.Count)总是大约是18100而不是预期的19000?
var list = new List<string>(19000);
List<Task> tl = new List<Task>(19000);
for (int q = 0; q < 19000; q++)
{
tl.Add(Task.Factory.StartNew(() =>
{
var k = "something";
list.Add(k);
}));
}
Task.WaitAll(tl.ToArray());
Console.WriteLine(list.Count);
答案 0 :(得分:7)
问题是List<T>
不是线程安全的。
您可以使用thread-safe中的System.Collections.Concurrent
个集合,并使用ConcurrentBag<>
类型。试试这个:
var list = new ConcurrentBag<string>();
List<Task> tl = new List<Task>(19000);
for (int q = 0; q < 19000; q++)
{
tl.Add(Task.Factory.StartNew(() =>
{
var k = "something";
list.Add(k);
}));
}
Task.WaitAll(tl.ToArray());
Console.WriteLine(list.Count);
还有其他线程安全类型,例如ConcurrentQueue<>
,ConcurrentStack<>
等。
另一方面,您可以使用lock
keyworkd来阻止其他线程访问同一个语句块。在这种情况下,您可以使用List<T>
和非线程安全对象作为示例:
对象必须对所有线程都可见,因此,您可以在类范围内声明它,对于示例:
private static object _sync = new object();
之后,尝试在需要修改时锁定此实例,对于示例:
var list = new List<string>(19000);
List<Task> tl = new List<Task>(19000);
for (int q = 0; q < 19000; q++)
{
tl.Add(Task.Factory.StartNew(() =>
{
lock(_sync)
{
var k = "something";
list.Add(k);
}
}));
}
Task.WaitAll(tl.ToArray());
Console.WriteLine(list.Count);
答案 1 :(得分:2)
List<T>
类不是线程安全的,请参阅MSDN
此类型的公共静态(在Visual Basic中为Shared)成员是线程安全的。不保证任何实例成员都是线程安全的。
在List上执行多个读取操作是安全的,但如果在读取集合时修改了集合,则可能会出现问题。要确保线程安全,请在读取或写入操作期间锁定集合。要使多个线程可以访问集合以进行读写,您必须实现自己的同步。对于具有内置同步的集合,请参阅System.Collections.Concurrent命名空间中的类。有关本质上线程安全的替代方法,请参阅ImmutableList类。
答案 2 :(得分:2)
List(T)
不是线程安全的。
https://msdn.microsoft.com/en-us/library/6sh2ey19%28v=vs.110%29.aspx
此类型的公共静态成员是线程安全的。任何实例 成员不保证是线程安全的。
因此,不保证从多个线程分配给它可以产生所需的结果。