2016-12-28 3 views
6

Es macht mich wenig schwer, das tatsächliche Verhalten in diesem Szenario zu verstehen. Was passiert, wenn die Aufgabe nicht ausgeführt wird, wenn SemaphoreSlim entsorgt wird? Es wirft mich exception- folgende System.ObjectDisposedException {"The semaphore has been disposed."}Was ist falsch im Code zu

ich eine Klassenbibliothek wie haben -

public class ParallelProcessor 
{ 
    private Action[] actions; 
    private int maxConcurrency; 

    public ParallelProcessor(Action[] actionList, int maxConcurrency) 
    { 
     this.actions = actionList; 
     this.maxConcurrency = maxConcurrency; 
    } 

    public void RunAllActions() 
    { 
     if (Utility.IsNullOrEmpty<Action>(actions)) 
      throw new Exception("No Action Found!"); 

     using (SemaphoreSlim concurrencySemaphore = new SemaphoreSlim(maxConcurrency)) 
     { 
      foreach (Action action in actions) 
      { 
       Task.Factory.StartNew(() => 
       { 
        concurrencySemaphore.Wait(); 
        try 
        { 
         action(); 
        } 
        finally 
        { 
         concurrencySemaphore.Release(); 
        } 
       }); 
      } 
     } 
    } 
} 

Und es wie-

class Program 
{ 
    static void Main(string[] args) 
    { 
     int maxConcurrency = 3; 
     Action[] actions = new Action[] {() => Console.WriteLine(1),() => Console.WriteLine(2),() => Console.WriteLine(3) }; //Array.Empty<Action>(); 

     ParallelProcessor processor = new ParallelProcessor(actions, maxConcurrency); 

     processor.RunAllActions(); 

     Console.ReadLine(); 
    } 
} 

Könnte jemand bitte etwas Licht auf sie überschüttet mit? Danke im Voraus.

Antwort

3

Ihr Semaphor befindet sich am Ende des using Blocks, wird aber von der noch laufenden Task verwendet, die darin erstellt wurde.
Ich würde empfehlen, die Semaphore bis zur Klassenstufe bewegt:

public class ParallelProcessor 
{ 
    private Action[] actions; 
    private SemaphoreSlim concurrencySemaphore; 

    public ParallelProcessor(Action[] actionList, int maxConcurrency) 
    { 
     this.actions = actionList; 
     concurrencySemaphore = new SemaphoreSlim(maxConcurrency); 
    } 

    public void RunAllActions() 
    { 
     if (Utility.IsNullOrEmpty<Action>(actions)) 
      throw new Exception("No Action Found!"); 

     foreach (Action action in actions) 
     { 
      Task.Factory.StartNew(() => 
       { 
        concurrencySemaphore.Wait(); 
        try 
        { 
         action(); 
        } 
        finally 
        { 
         concurrencySemaphore.Release(); 
        } 
       }); 
     } 
    } 
} 

oder einen alternativen Ansatz, bei dem RunAllActions blockiert, bis alle fertig sind:

public class ParallelProcessor 
{ 
    private Action[] actions; 
    private int maxConcurrency; 

    public ParallelProcessor(Action[] actionList, int maxConcurrency) 
    { 
     this.actions = actionList; 
     this.maxConcurrency = maxConcurrency; 
    } 

    public void RunAllActions() 
    { 
     if (Utility.IsNullOrEmpty<Action>(actions)) 
      throw new Exception("No Action Found!"); 

     using (var concurrencySemaphore = new SemaphoreSlim(maxConcurrency)) 
     { 
      Task.WaitAll(actions.Select(a => Task.Run(() => 
       { 
        concurrencySemaphore.Wait(); 
        try { a(); } 
        finally { concurrencySemaphore.Release(); } 
       })).ToArray()); 
     } 
    } 
} 
11

Das Problem Ihr using Aussage. Dies ist, wie Dinge geschehen:

  • Erstellen Sie die Semaphore
  • Aufgaben starten im Hintergrund laufen
  • Entsorgen Sie das Semaphore
  • Aufgaben versuchen die Semaphore zu verwenden ... aber nicht kann, weil es

Optionen angeordnet:

  • Gerade r emove die using Anweisung (so entsorgt man nicht von der Semaphore, aber das ist unwahrscheinlich, dass ein Problem zu sein, es sei denn, Sie verwenden diese wirklich stark)
  • Ihre Methode ändern zu blockieren (innerhalb der using-Anweisung), bis die gesamten Aufgaben wurden abgeschlossen, z von Parallel.ForEach anstelle Task.Factory.StartNew direkt
  • des Aufruf
  • Code Änderung der Semaphore in einer Aufgabe zu verfügen, die erst nach allen anderen Aufgaben ausführen haben
+0

Vielen Dank zur Erläuterung der Reihenfolge der Ausführung der Verwendung oder entfernen entfernen. Es ist mein Schlechter :(Ich habe die Fehler nicht bemerkt, wenn ich "using" und "Task" benutze. Nochmals danke. –

0

abgeschlossen Ich glaube, das Problem ist die concurrencySemaphore verfügen, das bereits in der using-Anweisung.

Die hauptsächliche Verwendung von Mit ist, wird es automatisch um zu versuchen und schließlich und endlich wird es das Objekt verfügt, die unter verwenden ist.

https://www.codeproject.com/Articles/6564/Understanding-the-using-statement-in-C

Die Lösung für Ihren Fall ist entweder die schließlich Aussage