如果我从发布代码开始会更容易:

static void Main(string[] args)
{
    List<double> testLst = new List<double>();
    for (int i = 0; i < 20000000; i++) { testLst.Add(i); }

我已经用 20,000,000 个元素填充了一个列表。我在任务管理器中看到该进程正在使用 ~300MB。如果我使用 foreach 循环遍历列表:
    foreach (var a in testLst.Take(10))
    {
        Console.WriteLine(a);
    }
}

内存使用不会增加(我在 Console.WriteLine 上放置了一个断点,正如我所说,我正在使用任务管理器测量它)。现在,如果我用 ConcurrentBag 替换 List:
static void Main(string[] args)
{
    ConcurrentBag<double> testCB = new ConcurrentBag<double>();
    for (int i = 0; i < 20000000; i++) { testCB.Add(i); }

    foreach (var a in testCB.Take(10))
    {
        Console.WriteLine(a);
    }
}

在 foreach 循环之前,内存使用量为 450~500MB。问题是:为什么如果在 foreach 循环内部使用跳转到 ~900MB?

与 List 相比,我希望 ConcurrentBag 消耗更多内存,但我不明白为什么迭代使用了这么多内存。

(我在类似但不同的场景中使用 ConcurrentBag,我知道在这种情况下使用它没有意义)

最佳答案

来自 ConcurrentBag.GetEnumerator docs(强调我的):



查看 source ,您可以看到它创建了包的副本:

public IEnumerator<T> GetEnumerator()
{
    // Short path if the bag is empty
    if (m_headList == null)
        return new List<T>().GetEnumerator(); // empty list

    bool lockTaken = false;
    try
    {
        FreezeBag(ref lockTaken);
        return ToList().GetEnumerator();
    }
    finally
    {
        UnfreezeBag(lockTaken);
    }
}

顾名思义,ToList() 返回一个 List<T>(它不是扩展方法,它是一个私有(private)成员函数)。

附带说明一下,return new List<T>().GetEnumerator(); 行并不漂亮……本来可以编写 return Enumerable.Empty<T>().GetEnumerator(); 来代替。

10-07 16:08