2010-08-23 3 views
6

Ich verwende die Klassen ConcurrentDictionary und ConcurrentQueue von .NET 4 im folgenden Code.Ist diese Kombination aus ConcurrentDictionary und ConcurrentQueue threadsicher?

Ist dieser Code threadsicher? Wenn nicht, wie kann ich es threadsicher machen?

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

Antwort

10

Wie @Femaref richtig ausgeführt hat, gibt es einige Fehler in Ihrem Code. Ich schlage vor, Sie die Vorteile der vielen Methoden, die von ConcurrentDictionary<K,V> angeboten nehmen Sie den Code Thread-sicher, ohne die Notwendigkeit für lock Aussagen zu machen:

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: Das ist genau das, was ich vorschlagen würde. –

+0

"so einfach wie das", große Antwort thx. – RuSh

-1

können Sie (und wahrscheinlich) laufen, um Probleme mit diesen Aussagen:

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

und

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

als ConcurrentDictionary können zwischen den geändert werden if-Anweisung und die Zuweisung/Dequeue. Verwenden Sie eine Sperre auf ein Sperrobjekt für die Teile des Codes, wie:

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 viele Methoden bietet so ** ** Sie nicht verwenden müssen 'Lock' . Das ist im Grunde der Sinn von ConcurrentDictionary. – dtb

+0

thx Femaref, ich wusste, dass ich ein Schloss hinzufügen musste, aber ich hatte gehofft, dass ConcurrentDictionary etwas eingebaut hat, ich kann es einfach nicht. – RuSh

+0

dtb, können Sie schreiben, welche Methode von ConcurrentDictionary kann meinen Code passen? – RuSh