当您希望清除它们时,.NET中的线程不会被清除

时间:2011-11-28 09:37:31

标签: c# .net

修改

好消息是下面解释的奇怪行为与ConcurrentBag无关,即:与并发包相关的线程最终被释放。然而,由于某些或其他原因,自己的线程仍然存在。在给出的示例代码中,我清楚地创建了一个线程并销毁所有引用。然而,垃圾收集不会捡起它。实际上,到目前为止,我发现线程完全被破坏的唯一时刻是并发包本身被收集(如果我不收集并发包,线程将保持活动状态),或者当我们创建许多其他线程时

ORIGINAL(原始问题和一些动机。此部分下面的源代码解释了重要方面)

这是我几个月前就ConcurrentBag(Possible memoryleak in ConcurrentBag?)问过的一个问题的复制品。显然,ConcurrentBag的行为并不应该如此,现在我担心一些正在运行的遗留代码的稳定性。在回答以下问题时,这个问题是对我的发现的回应:How can I free-up memory used by a Parallel.Task?

情景没有改变。我有一个处理消息的web服务。客户端可以使用一些公共API在消息中进行拍摄。这些请求将由来自.NET ThreadPool的线程进行处理,而ThreadPool又将消息添加到ConcurrentBag。接下来,有并发任务消耗来自ConcurrentBag的消息并处理它们。有高峰时段添加了许多消息,许多消息被消耗,有时没有人做任何事情。因此,线程池本身在运行时间内会非常广泛地改变其活动线程的数量(期望的运行时间是'永远')。

现在事实证明,一旦线程调用ConcurrentBag.Add(或ConcurrentBag上的任何方法)。通过ConcurrentBag内部保存的引用保持线程活动,并且只有当GC实际清除concurrentBag本身时才释放该线程。在我的场景中,这将导致无数的“浪费”线程,因为ConcurrentBag在整个应用程序的生命周期中都处于活动状态,所以它们不会随着时间的推移而被清理。

之前给出的简单清空袋子的解决方案也无济于事,因为没有问题。问题(可能是ConcurrentBag没有调用它为当前Thread保存的ThreadLocal上的Dispose,因为ConcurrentBag无法知道线程何时结束)。 (但是每当你再次进入行李时它应该进行清理。)

那就是说,我们是否应该在大多数情况下停止使用ConcurrentBag,或者我是否可以添加解决方案以解决此问题?

我的测试代码:

        Action collectAll = () =>
        {
            GC.Collect();
            GC.WaitForPendingFinalizers();
            GC.Collect();
        };

        ConcurrentBag<object> bag = new ConcurrentBag<object>();

        Thread producerThread = new Thread(new ThreadStart(delegate
        {
            // produce 1 item
            bag.Add(new object());
        }));

        // create a weakreference to the newly created thread so we can track its status
        WeakReference trackingReference = new WeakReference(producerThread);

        // start the thread and let it complete 
        producerThread.Start();
        producerThread.Join();

        // thread can now be set to null, after a full GC collect, we assume that the thread is gone
        producerThread = null;
        collectAll();

        Console.WriteLine("Thread is still alive: " + trackingReference.IsAlive); // returns true

        // consume all items from the bag and collect again, the thread should surely be disposed by now
        object output;
        bag.TryTake(out output);
        collectAll();

        Console.WriteLine("Thread is still alive: " + trackingReference.IsAlive); // returns true

        // ok, finally remove all references to the Bag
        bag = null;
        collectAll();

        Console.WriteLine("Thread is still alive: " + trackingReference.IsAlive); // returns false 

0 个答案:

没有答案