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);

「Exited」コールバックで、リソースを破棄します:

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」オブジェクトがまだありますが、どこで処分できるかわかりません.. exited メソッドがトリガーされ、_queue が「Item」オブジェクトをキューから削除しました。

ドキュメントを読むと、concurrentqueue が参照をキューにコピーします。

メモリリークがどこにあるかを見つけるのを手伝ってもらえますか?

4

2 に答える 2

6

標準の .NET Queue とは異なり、Dequeue() を呼び出しても、オブジェクトへの参照はコレクションから削除されません。この動作はバージョン 4.0 からバージョン 4.5 に変更されています (私はこれを読みましたが、テストしていません)。これはバグではなく、フレームワーク チームがスレッド セーフの設計の一環として行った意識的な設計上の決定です。 、列挙可能なコレクション。

この記事には、StrongBox を使用して ConcurrentQueue に入るオブジェクトをラップすることによる回避策など、詳細が記載されています。4.5 フレームワークに移行できるようになるまでは、これが適切な回避策になるはずです。

于 2013-01-22T14:46:27.707 に答える
3

Concurrent Queue の実装を見てきました。Dequeue() が呼び出された後、キューがオブジェクトへの参照を保持する場合があります。

コンカレント キューは、セグメントを使用してデータを格納します。セグメントの 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 に答える