2016-12-28 14 views
6

Nie jest mi trudno zrozumieć rzeczywiste zachowanie w tym scenariuszu. Co się właściwie dzieje, aby nie wykonać zadania, kiedy powinno, ale później, kiedy SemaphoreSlim zostanie rozwiązany. To rzuca mi następujące exception- System.ObjectDisposedException {"The semaphore has been disposed."}Co jest nie tak w kodzie do

Mam Biblioteka klasy jak -

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

i używając go jak-

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

może ktoś proszę obsypany trochę światła na to? Z góry dziękuję.

Odpowiedz

3

Twój semafor jest umieszczony na końcu bloku using, ale używany przez wciąż działające zadanie utworzone wewnątrz niego.
Polecam przesuwając semafora do poziomu klasy:

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

lub alternatywne podejście, gdzie RunAllActions będzie blokował aż wszystkie są zrobione:

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

Problemem jest twój rachunek using. Jest to, jak rzeczy się dzieją:

  • Tworzenie semafor
  • zadania zaczynają biec w tle
  • dysponowania semafora
  • Zadania spróbuj użyć semafora ... ale nie mogę, bo to wyrzucać

Opcje:

  • Wystarczy r suń oświadczenie using (więc nie wyrzucać semafora, ale to jest mało prawdopodobne, aby być problem, chyba, że ​​używasz tego naprawdę mocno)
  • zmienić metodę blokowania (wewnątrz rachunku using) aż do całkowitego zadania zostały zakończone, np za pomocą Parallel.ForEach zamiast dzwonić Task.Factory.StartNew bezpośrednio
  • Zmień swój kod do dysponowania semafora w zadania, które zostaną wykonane dopiero po wszystkich innych zadań zakończyły
+0

dziękuję za wyjaśnienie kolejność realizacji. To jest moje złe :(Nie zauważyłem błędów związanych z używaniem "używania" i "zadania". –

0

Wierzę, że problem jest wyrzucać concurrencySemaphore który jest już w instrukcji using.

Głównym zastosowaniem Korzystanie jest, zostanie ona automatycznie dodać spróbować i wreszcie, w końcu będzie wyrzucać obiekt, który znajduje się pod użyciu.

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

Rozwiązanie Twoim przypadku jest albo usunąć stosując lub usunąć finally