2012-10-10 14 views
6

Ich verwende eine Producer/Consumer Pattern mit einer System.Collection.Concurrent.BlockingCollection<DataTable>, um Daten von einer Datenbank (Hersteller) abzurufen und einen Lucene-Index für die Daten (Consumer) zu erstellen.Die .NET Concurrent BlockingCollection hat ein Speicherleck?

Der Producer greift 10000 Datensätze gleichzeitig und fügt das Set dem BlockingCollection<DataTable> hinzu. Der Verbraucher (der ein wenig langsamer ist) greift dann diese 10000 und erstellt einen Index.

Die blockierende Sammlung ist auf 5 <DataTable> von jeweils 10000 Zeilen begrenzt.

Am Anfang läuft das Programm gut, aber nachdem es ungefähr 150000 Reihen in mir kommt, bemerkte ich, dass mein Computerspeicher maximiert ist und es zu einem Kriechen verlangsamt.

Es scheint, dass die BlockingCollection den zugrunde liegenden Array-Slot nach der Übernahme des Elements nicht auf null setzen kann.

Code:

private static LuceneIndex index; 
    private static BlockingCollection<DataTable> blockingCol; 

    private static void Producer() 
    { 
     while (true) 
     { 
      //...get next 10000 rows 
      DataTable data = GetNextSet(); 
      if(data.Row.Count > 0) 
       blockingCol.Add(products); 
      else 
       break; 
     } 
    } 

    private static void Consumer() 
    { 
     while (!BlockingCol.IsCompleted || BlockingCol.Count > 0) 
     { 
      DataTable data = blockingCol.Take(); 
      index.UpdateIndex(GetLuceneDocs(data)); 
     } 
    } 


public static void Main(System.String[] args) 
{ 
      index = new LuceneIndex(); 
      blockingCol = new BlockingCollection<DataTable>(2); 
      // Create the producer and consumer tasks. 
      Task Prod = new Task(Producer); 
      Task Con = new Task(Consumer); 
      // Start the tasks. 
      Con.Start(); 
      Prod.Start(); 
      // Wait for both to finish. 
      try 
      { 
       Task.WaitAll(Con, Prod); 
      } 
      catch (AggregateException exc) 
      { 
       Console.WriteLine(exc); 
      } 
      finally 
      { 
       Con.Dispose(); 
       Prod.Dispose(); 
       blockingCol.Dispose(); 
      } 
} 

Kann jemand bestätigen diese Suspension ablehnen? Und gibt es Arbeit?

Antwort

8

Ja, ich kann das bestätigen. Sie sind nicht auf .NET 4.5, oder? Es soll dort behoben werden (und Ihre Kommentare unter dieser Antwort scheinen dies zu bestätigen).

Wie auch immer, schreibe dir einen Wrapper um eine DataTable und lösche diesen Wrapper, wenn du mit der Tabelle fertig bist. Das macht es für GC geeignet. Der Wrapper wird nicht frühzeitig erkannt, ist aber winzig.

class Wrapper<T> { public T Item; } 
+0

Ich bin auf .net 4.5. Ich benutze tatsächlich Unterschallsammlung statt Datentabelle. Ich habe in diesem Beispiel einfach nur Datenblätter eingefügt. Ich werde deine Lösung versuchen. – NSjonas

+0

Ich denke, es ist dann nicht behoben (wahrscheinlich erinnerte ich mich). Ich habe dieses Problem jedoch selbst gesehen. – usr

+1

Wrapper Wrapper = BlockingCol.Take(); // tun Sachen wrapper.Item = null; Das meinst du richtig? – NSjonas