2016-05-15 8 views
2

Ich habe ein Beispiel unten gezeigt. Ich kann nicht herausfinden, warum das Are auch für die Warteschlange gesperrt ist, während die beiden kohärenten Algorithmen mit demselben Objekt vollständig gesperrt sind.Warum ist Warteschlangensperre in diesem Beispiel erforderlich

Es wird gesagt, dass Warteschlangensperre erforderlich ist.

using System; 
using System.Threading; 
using System.Collections; 

public class CrudeThreadPool 
{ 
    static readonly int MaxWorkThreads = 4; 
    static readonly int WaitTimeout = 2000; 

    public delegate void WorkDelegate(); 

    public CrudeThreadPool() { 
     stop = false; 
     workLock = new Object(); 
     workQueue = new Queue(); 
     threads = new Thread[ MaxWorkThreads ]; 

     for(int i = 0; i < MaxWorkThreads; ++i) { 
      threads[i] = 
       new Thread(new ThreadStart(this.ThreadFunc)); 
      threads[i].Start(); 
     } 
    } 

    private void ThreadFunc() { 
     lock(workLock) { 
      do { 
       if(!stop) { 
        WorkDelegate workItem = null; 
        if(Monitor.Wait(workLock, WaitTimeout)) { 

         lock(workQueue.SyncRoot) { 
          workItem = 
           (WorkDelegate) workQueue.Dequeue(); 
         } 
         workItem(); 
        } 
       } 
      } while(!stop); 
     } 
    } 

    public void SubmitWorkItem(WorkDelegate item) { 
     lock(workLock) { 
      lock(workQueue.SyncRoot) { 
       workQueue.Enqueue(item); 
      } 

      Monitor.Pulse(workLock); 
     } 
    } 

    public void Shutdown() { 
     stop = true; 
    } 

    private Queue   workQueue; 
    private Object  workLock; 
    private Thread[]  threads; 
    private volatile bool stop; 
} 

public class EntryPoint 
{ 
    static void WorkFunction() { 
     Console.WriteLine("WorkFunction() called on Thread {0}", 
         Thread.CurrentThread.ManagedThreadId); 
    } 

    static void Main() { 
     CrudeThreadPool pool = new CrudeThreadPool(); 
     for(int i = 0; i < 10; ++i) { 
      pool.SubmitWorkItem(
       new CrudeThreadPool.WorkDelegate(
           EntryPoint.WorkFunction)); 
     } 

     Thread.Sleep(1000); 

     pool.Shutdown(); 
    } 
} 

Was ist der Grund für das Sperren der Warteschlange?

+0

Woher hast du diesen Code und warum glaubst du, dass er überhaupt funktioniert? – Evk

+0

Dieser Code funktioniert definitiv. Es kommt von einem Lehrbuch. –

+1

Wenn Sie versuchen würden, diesen Code mehrmals auszuführen, werden Sie feststellen, dass er fehlerhaft ist. Sie stellen 10 Elemente in die Warteschlange, aber die zufällige Anzahl der Elemente wird verarbeitet. Manchmal 10, manchmal 8, manchmal 4, manchmal 0. Die Person, die es geschrieben hat, hat nicht verstanden, was er tat (besonders wie Monitor.Pulse und Monitor.Wait funktionieren), also nicht viel Grund zu diskutieren, warum er den Zugriff auf die Warteschlange sperrt. – Evk

Antwort

1

Die innere Sperre ist eigentlich nicht notwendig, denn solange die Wait nicht wieder erreicht wird, wird die Sperre gehalten und blockiert alle Produzenten. Daher sollte diese Arbeit:

private void ThreadFunc() { 
    do { 
     if(!stop) { 
      WorkDelegate workItem = null; 
      lock(workLock) { 
       if(Monitor.Wait(workLock, WaitTimeout)) { 
        workItem = (WorkDelegate) workQueue.Dequeue(); 
       } 
      } 
      if (workItem != null) workItem(); 
     } 
    } while(!stop); 
} 

public void SubmitWorkItem(WorkDelegate item) 
{ 
    lock(workLock) { 
     workQueue.Enqueue(item); 

     Monitor.Pulse(workLock); 
    } 
} 

Joseph Albahari's site ist ein ehrfürchtiges Referenz für Threading-Szenarien. Obwohl dies ein klassisches Producer/Consumer-Szenario ist, würde ich empfehlen, einen BlockingCollection zu verwenden.

+0

Dies wird nur die Ausnahme werfen, dass die Warteschlange leer ist - das ist alles. – Evk

+0

@Evk Sehr wahr, aktualisiert, um dies zu beheben. – Slugart

+0

Leider wurde das nicht besser - jetzt, wenn die Warteschlange leer ist, wiederholst du kontinuierlich einen Zyklus und tust nichts mehr CPU-Ressourcen verschwenden :) Im Beispielcode Monitor.Wait und Monitor.Pulse werden für die Signalisierung verwendet. – Evk