9

使用时出现内存泄漏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”对象。

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

你能帮我找出内存泄漏在哪里吗?

4

2 回答 2

6

与标准 .NET 队列不同,调用 Dequeue() 不会从集合中删除对对象的引用。虽然此行为已从 4.0 版本更改为 4.5 版本(我已阅读此内容,但尚未对其进行测试),但它不是错误,而是框架团队在设计线程安全时做出的有意识的设计决定,可枚举的集合。

本文包含更多信息,包括使用 StrongBox 包装进入 ConcurrentQueue 的对象的解决方法。在您可以迁移到 4.5 框架之前,这应该是一个合适的解决方法。

于 2013-01-22T14:46:27.707 回答
3

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

并发队列使用 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。

于 2014-12-16T16:07:10.627 回答