2010-08-23 7 views
6

Estoy usando las clases ConcurrentDictionary y ConcurrentQueue de .NET 4 en el siguiente código.¿Esta combinación de ConcurrentDictionary y ConcurrentQueue es segura para subprocesos?

¿Este código es seguro para subprocesos? Si no, ¿cómo puedo hacerlo seguro para subprocesos?

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

Demostración:

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

Respuesta

10

Como @Femaref de manifiesto acertadamente, hay algunos defectos en su código. Le sugiero que tome ventaja de los muchos métodos ofrecidos por ConcurrentDictionary<K,V> Para que el código flujos seguros sin necesidad de lock declaraciones:

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: Esto es exactamente lo que iba a sugerir. –

+0

"tan simple como eso", gran respuesta thx. – RuSh

-1

Puede (y probablemente) ejecutar en problemas con estas declaraciones:

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

y

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

como el ConcurrentDictionary puede ser cambiado entre el if-statement y Assignment/Dequeue. Utilizar un bloqueo en un objeto de bloqueo para aquellas partes del código, como:

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 ofrece muchos métodos para que no ** ** tiene que usar 'lock' . Ese es básicamente el objetivo de ConcurrentDictionary. – dtb

+0

Gracias Femaref, sabía que necesitaba agregar un candado, pero esperaba que ConcurrentDictionary tuviera algo en común, simplemente no puedo. – RuSh

+0

dtb, ¿puede publicar qué método de ConcurrentDictionary puede ajustarse a mi código? – RuSh

Cuestiones relacionadas