2012-10-10 11 views
6

Sto utilizzando un Producer/Consumer Pattern con un System.Collection.Concurrent.BlockingCollection<DataTable> per recuperare dati da un database (produttore) e creare un indice Lucene sui dati (consumatore).Il .Net Concurrent BlockingCollection ha una perdita di memoria?

Il produttore acquisisce 10000 record alla volta e aggiunge set allo BlockingCollection<DataTable>. Il consumatore (che è un po 'più lento) afferra quei 10000 e crea un indice.

La raccolta di blocchi è limitata a 5 <DataTable> di 10000 righe ciascuno.

All'inizio il programma funziona alla grande, ma dopo aver ottenuto circa 150000 righe ho notato che la memoria del mio computer è al massimo e rallenta a passo d'uomo.

Sembra che BlockingCollection non riesca a impostare lo slot dell'array sottostante su null dopo l'acquisizione dell'elemento.

Codice:

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(); 
      } 
} 
Can

chiunque conferma di rifiutare questa sospensione? E c'è qualche problema?

risposta

8

Sì, lo posso confermare. Non sei su .NET 4.5, vero? Dovrebbe essere risolto lì (e i tuoi commenti sotto questa risposta sembrano confermarlo).

In ogni caso, scriviti un involucro attorno a uno DataTable e cancella quel wrapper quando hai finito con il tavolo. Ciò lo rende idoneo per GC. Il wrapper non sarà GC presto ma è piccolo.

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

Sono su .net 4.5. In realtà sto usando una collezione subsonica anziché datatable. Ho appena incluso i datatables in questo esempio per semplicità. Proverò la tua soluzione. – NSjonas

+0

Suppongo che non sia corretto allora (probabilmente mi sono ricordato). Ho visto questo problema me stesso, però. – usr

+1

Wrapper wrapper = BlockingCol.Take(); // do stuff wrapper.Item = null; Questo è ciò che intendi giusto? – NSjonas