循环计数!= list.Count -填充列表在新的线程

本文关键字:string 线程 列表 填充 list Count 循环 | 更新日期: 2023-09-27 17:50:16

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

循环计数!= list.Count -填充列表<string>在新的线程

问题是List<T>不是线程安全的。

任务并行性

您可以在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(object)

另一方面,您可以使用lock关键字来阻止其他线程访问同一语句块。在这种情况下,您可以使用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);

List<T>类不是线程安全的,参见MSDN

<标题>线程安全此类型的Public static(在Visual Basic中共享)成员是线程安全的。不能保证任何实例成员都是线程安全的。

在List上执行多个读操作是安全的,但如果在读取集合时修改集合,则可能发生问题。为确保线程安全,请在读或写操作期间锁定集合。要使一个集合能够被多个线程访问以进行读写,您必须实现自己的同步。关于内置同步的集合,请参阅System.Collections.Concurrent命名空间中的类。有关固有线程安全的替代方法,请参阅immutableelist类。

List(T)不是线程安全的。

https://msdn.microsoft.com/en-us/library/6sh2ey19%28v=vs.110%29.aspx

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

从多个线程分配给它的结果不能保证产生期望的结果