2010-08-23 9 views
6

Sto utilizzando le classi ConcurrentDictionary e ConcurrentQueue da .NET 4 nel seguente codice.Questa combinazione di ConcurrentDictionary e ConcurrentQueue è protetta da thread?

È un codice thread-safe? In caso contrario, come posso renderlo thread-safe?

public class Page 
{ 
    public string Name {get; set; } 
} 

public class PageQueue 
{ 
    private ConcurrentDictionary<int, ConcurrentQueue<Page>> pages = 
     new ConcurrentDictionary<int, ConcurrentQueue<Page>>(); 

    public void Add(int id, Page page) 
    { 
     if (!this.pages.ContainsKey(id)) 
      this.pages[id] = new ConcurrentQueue<Page>(); 

     this.pages[id].Enqueue(page); 
    } 

    public Page GetAndRemove(int id) 
    { 
     Page lp = null; 

     if(this.pages.ContainsKey(id)) 
      this.pages[id].TryDequeue(out lp); 

     return lp; 
    } 
} 

Demo:

public class Demo 
{ 
    public void RunAll() 
    { 
     for (int i = 0; i < 10; i++) 
      Task.Factory.StartNew(() => Run()); 
    } 

    public void Run() 
    { 
     PageQueue pq = new PageQueue(); 
     pq.Add(1, new Page()); 

     pq.GetAndRemove(1); 
    } 
} 

risposta

10

Come @Femaref giustamente osservato, ci sono alcuni difetti nel codice. Vi suggerisco di approfittare dei molti metodi offerti da ConcurrentDictionary<K,V> per rendere il codice thread-safe senza la necessità di lock dichiarazioni:

public class PageQueue 
{ 
    private ConcurrentDictionary<int, ConcurrentQueue<Page>> pages = 
     new ConcurrentDictionary<int, ConcurrentQueue<Page>>(); 

    public void Enqueue(int id, Page page) 
    { 
     var queue = this.pages.GetOrAdd(id, _ => new ConcurrentQueue<Page>()); 

     queue.Enqueue(page); 
    } 

    public bool TryDequeue(int id, out Page page) 
    { 
     ConcurrentQueue<Page> queue; 

     if (this.pages.TryGetValue(id, out queue)) 
     { 
      return queue.TryDequeue(out page); 
     } 

     page = null; 
     return false; 
    } 
} 
+0

+1: questo è esattamente quello che stavo per suggerire. –

+0

"così semplice", ottima risposta. – RuSh

-1

È possibile (e probabilmente sarà) eseguito in problemi con queste affermazioni:

if (!this.pages.ContainsKey(id)) 
     this.pages[id] = new ConcurrentQueue<Page>(); 

e

if(this.pages.ContainsKey(id)) 
     this.pages[id].TryDequeue(out lp); 

come il ConcurrentDictionary può essere cambiato tra il if-statement e Assignment/Dequeue. Utilizzare un blocco su un oggetto di blocco per quelle parti del codice, come:

public class PageQueue 
{ 
    private ConcurrentDictionary<int, ConcurrentQueue<Page>> pages = new ConcurrentDictionary<int, ConcurrentQueue<Page>>(); 
    private object locker = new object(); 

    public void Add(int id , Page page) 
    { 
     lock(locker) 
     { 
      if (!this.pages.ContainsKey(id)) 
       this.pages[id] = new ConcurrentQueue<Page>(); 
     } 

     this.pages[id].Enqueue(page); 
    } 

    public Page GetAndRemove(int id) 
    { 
     Page lp = null; 

     lock(locker) 
     { 
      if(this.pages.ContainsKey(id)) 
      this.pages[id].TryDequeue(out lp); 
     } 

     return lp; 
    } 
} 
+1

ConcurrentDictionary offre molti metodi in modo da ** non ** è necessario utilizzare 'lock' . Questo è fondamentalmente l'intero punto di ConcurrentDictionary. – dtb

+0

thx Femaref, sapevo che avevo bisogno di aggiungere un lucchetto, ma speravo che ConcurrentDictionary avesse qualcosa in esso, non posso. – RuSh

+0

dtb, puoi pubblicare quale metodo di ConcurrentDictionary può adattare il mio codice? – RuSh

Problemi correlati