ConcurrentQueue导致内存泄漏

时间:2012-12-26 13:38:06

标签: c# memory-leaks

使用ConcurrentQueue时发生内存泄漏:

requestObject request = xxx;

Item obj= new Item ();
obj.MessageReceived += obj_MessageReceived;
obj.Exited += obj_Exited;

request.Key = obj.Key;

obj.AddRequest(request);

_queue.TryAdd(obj.Key, obj);

在“退出”回调中,我处置资源:

void LiveSphere_Exited(string key)
{
    Item instance;

    _queue.TryRemove(key, out instance);

    Task.Factory.StartNew(() =>
    {
        var wait = new SpinWait();
        while (instance.MessageCount > 0)
        {
            wait.SpinOnce();
        }
    })
    .ContinueWith((t) =>
    {
         if (instance != null)
         {
             //Cleanup resources
             instance.MessageReceived -= obj_MessageReceived;
             instance.Exited -= obj_Exited;
             instance.Dispose();
             instance = null;
         }
    });
}

当我分析代码时,我仍然有一个根引用的“Item”对象,但我不知道我可以在哪里处理..., 触发退出的方法,_queue已从队列中删除“Item”对象。

当我阅读文档时,并发队列将引用复制到队列中。

你能帮我找出内存泄漏的位置吗?

2 个答案:

答案 0 :(得分:6)

与标准.NET队列不同,调用Dequeue()不会从集合中删除对象的引用。虽然这种行为已经从4.0版本变为4.5版本(我已经读过这篇文章,但还没有测试过),但这不是一个错误,而是框架团队作为设计线程安全的一部分做出的有意识的设计决策,可枚举的集合。

This article有更多信息,包括使用StrongBox来封装进入ConcurrentQueue的对象的变通方法。这应该是一个合适的解决方法,直到你可以转到4.5框架。

答案 1 :(得分:2)

我看过并发队列的实现。在某些情况下,队列将在调用Dequeue()后保存对象的引用。

Concurrent Queue使用Segments存储数据。它是该段的TryRemove方法的一部分:

// If there is no other thread taking snapshot (GetEnumerator(), ToList(), etc), reset the deleted entry to null.
// It is ok if after this conditional check m_numSnapshotTakers becomes > 0, because new snapshots won't include 
// the deleted entry at m_array[lowLocal]. 
if (m_source.m_numSnapshotTakers <= 0)
{
    m_array[lowLocal] = default(T); //release the reference to the object. 
} 

因此,当您有一个不同的线程同时枚举一个对象时,对象的队列将不会被设置为null。

相关问题