2013-07-12 13 views
11

Niedawno natknąłem się na przykład dławienia wątków dla połączeń asynchronicznych/oczekujących. Po analizie i graniu z kodem na moim komputerze, wymyśliłem nieco inny sposób robienia tego samego. Nie jestem pewien, czy to, co dzieje się pod maską, jest takie samo, czy też warto zwrócić uwagę na subtelne różnice?Ograniczanie wątków semaforowych z asynchronicznym/oczekiwanym

Oto kod na podstawie oryginalnego przykład:

private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(5); 

public async Task CallThrottledTasks() 
{ 
    var tasks = new List<Task>(); 

    for (int count = 1; count <= 20; count++) 
    { 
     await _semaphore.WaitAsync(); 

     tasks.Add(Task.Run(async() => 
      { 
       try 
       { 
        int result = await LongRunningTask(); 
        Debug.Print(result.ToString()); 
       } 
       finally 
       { 
        _semaphore.Release(); 
       } 
      })); 
    } 

    await Task.WhenAll(tasks); 

    Debug.Print("Finished CallThrottledTasks"); 
} 

A oto moje zdanie na ten sam kod:

private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(5); 

public async Task CallThrottledTasks() 
{ 
    var tasks = new List<Task>(); 

    for (int count = 1; count <= 20; count++) 
    { 
     await _semaphore.WaitAsync(); 

     tasks.Add(LongRunningTask().ContinueWith(t => 
     { 
      try 
      { 
       int result = t.Result; 
       Debug.Print(result.ToString()); 
      } 
      finally 
      { 
       _semaphore.Release(); 
      } 
     })); 
    } 

    await Task.WhenAll(tasks); 

    Debug.Print("Finished CallThrottledTasks"); 
} 

Jestem chyba daleko, ale wydaje się to zadanie. Podejście do uruchamiania polega na utworzeniu zadania do uruchamiania funkcji LongRunningTask(), a następnie dodaje kontynuację drukowania wyniku, podczas gdy moje podejście pomija zadanie utworzone przez Task.Run i jest w rezultacie nieco bardziej szczupłe. Czy to jest dokładne, czy też jestem poza bazą?

Odpowiedz

12

Nie jest to dużo chudszy, tylko trochę. Zazwyczaj unikam kodu ContinueWith w kodzie async, ponieważ await jest czystszy i ma więcej przyjaznej domyślnej semantyki async. Najpierw należy zoptymalizować czas programowania, a następnie zoptymalizować pod kątem innych czynników.

Twój kod nieznacznie zmienia semantykę: w oryginalnym kodzie, LongRunningTask został wykonany z kontekstu puli wątków, aw twoim kodzie jest wykonywany z kontekstu CallThrottledTasks. Twój kod nie będzie też propagował wyjątków od LongRunningTask; Task<T>.Result zawija wyjątki w AggregateException, podczas gdy await nie będzie wykonywać żadnego zawijania.

+0

Dziękuję. Jest to dokładnie to, czego szukam: efekty uboczne wdrożenia, które prześlizgują się przez szczeliny. Wygląda na to, że jest dużo tego z oczekiwaniem. Zrobię trochę pracy nóg, w jaki sposób traktowane są wyjątki. Jestem zaznajomiony z AggregateException z TPL, ale nie miałem wystarczająco dużo rąk do tego. – AFM

+2

Jest to w rzeczywistości więcej przypadków resztek z licencji TPL, które są - bardzo rzadko - przydatne w przypadku "asynchronizowania", ale częściej wchodzą w drogę. Np. Konstruktor 'Task',' Start', 'Wait',' Result', 'ContinueWith',' WaitAll' i 'WaitAny' są przeznaczone do * równoległego * (nie * asynchronicznego *) programowania i powinny być unikane w świat "asynchroniczny", chyba że naprawdę wiesz, co robisz. –