使用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”对象。
当我阅读文档时,并发队列将引用复制到队列中。
你能帮我找出内存泄漏的位置吗?
答案 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。