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

Leg*_*nds 2 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);
Run Code Online (Sandbox Code Playgroud)

Fel*_*ani 7

问题是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);
Run Code Online (Sandbox Code Playgroud)

还有其他的线程安全的类型,如ConcurrentQueue<>,ConcurrentStack<>等.

处理非线程安全对象 - 锁定(对象)

另一方面,您可以使用lockkeyworkd来阻止其他线程访问同一个语句块.在这种情况下,您可以使用List<T>和非线程安全对象,用于示例:

该对象必须对所有线程都可见,因此,您可以在类范围内声明它,对于示例:

private static object _sync = new object();
Run Code Online (Sandbox Code Playgroud)

在它之后,尝试在需要修改时锁定此实例,对于示例:

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);
Run Code Online (Sandbox Code Playgroud)

  • 创建一大堆线程来完成工作,然后将它们全部同步,使得不超过一个可以做任何工作,这是积极的非生产性的.问题的解决方案不是"锁定",而是首先不要将工作并行化. (2认同)