在ConcurrentBag可能memoryleak?ConcurrentBag、memoryleak

2023-09-02 01:37:07 作者:织一束月色

我一直在读入新的并发集合和esspecially的ConcurrentBag了我的注意。由于ConcurrentBag内部保持用它跟踪的项每个单独的线程上的本地集合,这意味着,当线程本身失控的范围,它仍然会在存储器由ConcurrentBag引用。这反过来又意味着两个存储器声称的螺纹,以及天然资源? (原谅我不知道的.NET线程对象的确切内部工作)

I've been reading into the new concurrent collections and esspecially the ConcurrentBag got my attention. Since the ConcurrentBag internally holds a local set on each individual thread using it to keep track of the items, this means that when the thread itself gets out of scope, it will still be referenced in memory by the ConcurrentBag. This in turn means both memory claimed by the thread, as well as native resources? (excuse me for not knowing the exact inner workings of the .NET thread object)

我可以假设一个用例,其中有1对多线程的web服务的全球ConcurrentBack,你有很多客户加入的任务。这些任务由线程池线程加入。现在的线程池是管理线程一个非常有效的方式,但它并根据工作量删除和创建线程。因此,这样的web服务可以在次找到本身麻烦因为底层袋仍参照的许多应-销毁线程

I can assume a usecase where you have 1 global ConcurrentBack for a multithreaded webservice where you have alot of clients adding tasks. These tasks are added by threads on the threadpool. Now the threadpool is a very efficient way to manage threads but it does remove and create Threads based on the amount of work. Therefore, such a webservice can at times find itself in trouble since the underlying bag is still referencing to many should be-destroyed threads.

我创建了一个快速的应用程序,以测试这种行为:

I created a quick app to test this behavior:

    static ConcurrentBag<int> bag = new ConcurrentBag<int>();
    static void FillBag() { for (int i = 0; i < 100; i++) { bag.Add(i); } }
    static void PrintState() { Console.WriteLine("Bag size is: {0}", bag.Count); }
    static void Main(string[] args)
    {
        var remote = new Thread(x =>
        {
            FillBag();
            PrintState();
        });
        // empty bag
        PrintState();
        // first 100 items are added on main thread
        FillBag();
        PrintState();
        // second 100 items are added on remote thread
        remote.Start();
        remote.Join();
        // since the remote thread is gone out of scope, what happened to its local storage which is part of the bag?
        PrintState();
        // now force a cleanup
        WeakReference weakRemoteReference = new WeakReference(remote); 
        remote = null;
        GC.Collect();
        GC.WaitForPendingFinalizers();
        // Now check if the thread still exists
        if (weakRemoteReference.IsAlive)
            Console.WriteLine("Remote thread still exists");
        PrintState();
        Console.ReadLine();

和输出印证了我的故事:

And the output confirms my story:

Bag size is: 0
Bag size is: 100
Bag size is: 200
Bag size is: 200
Remote thread still exists
Bag size is: 200

时的这种行为可以预料的,没有我犯了一个错误在我的测试,也可以这被认为是一个设计缺陷?

Is this behavior to be expected, did i make a mistake in my test or can this be considered a design flaw?

推荐答案

ConcurrentBag 确实让事情在线程本地存储,而如果你放弃线程它的可以的会导致内存泄漏。然而,实现能够偷,从一个线程的列表中的项目给予另一个线程。你可以看到这个动作,如果你写了以下内容:

The ConcurrentBag does indeed keep things in thread local storage, and if you abandon threads it can cause a memory leak. However, the implementation is able to "steal" items from one thread's list to give to another thread. You can see this in action if you write the following:

ConcurrentBag<int> MyBag = new ConcurrentBag<int>();

void DoIt()
{
    for (int i = 0; i < 10; ++i)
    {
        MyBag.Add(i);
    }

    ThreadPool.QueueUserWorkItem(EmptyBag);

    Console.Write("Press Enter:");
    Console.ReadLine();

    Console.WriteLine("{0} items in bag", MyBag.Count);
}

void EmptyBag(object state)
{
    int take;
    while (MyBag.TryTake(out take))
    {
        Console.WriteLine(take);
    }
    Console.WriteLine("Bag is empty");
}

如果你运行该程序,等待袋为空的消息,然后敲回车,你会看到袋子确实是空的。

If you run that program and wait until the "Bag is empty" message before you hit Enter, you'll see that the bag is indeed emptied.

所以,只要有一个线程从包里阅读,它的将会的最终被清空。即使所有的物品都被其它线程补充说。

So, as long as there's one thread reading from the bag, it will be emptied eventually. Even if all the items were added by other threads.

所以,是的,有可能存在内存泄漏。但在实践中,如果将多个线程访问的袋子,很可能不是一个问题。

So, yes, there's a possible memory leak. In practice, though, if multiple threads are accessing the bag, it's likely not a concern.

相关推荐