循环计数!= list.Count - 在新线程中填充List <string>

时间:2015-05-21 20:06:11

标签: c# asynchronous

为什么以下代码的结果(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);

3 个答案:

答案 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

  

此类型的公共静态成员是线程安全的。任何实例   成员不保证是线程安全的。

因此,不保证从多个线程分配给它可以产生所需的结果。