2013-07-12 3 views
11

Недавно я наткнулся на пример дросселирования потоков для вызовов async/await. После анализа и игры с кодом на моей машине я придумал несколько другой способ сделать то же самое. Что я не уверен в том, что то, что происходит под капотом, почти одинаково или если есть какие-то тонкие различия, которые стоит отметить?Семафорная дроссельная заслонка с асинхронным/ожиданием

Вот код, основанный на оригинальном примере:

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

И вот мое взятие на том же коде:

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

Я, вероятно, далеко, но кажется, как Task. Подход Run создает задачу для запуска LongRunningTask(), а затем добавляет продолжение для печати результата, тогда как мой подход обходит задачу, созданную Task.Run, и в результате становится немного более компактным. Является ли это точным или я отсюда?

ответ

12

Это не намного более компактный, немного. Обычно я избегаю ContinueWith в async код, потому что await чище и имеет более async -дружественная по умолчанию семантика. Сначала оптимизируйте время разработки, а затем оптимизируйте для других соображений.

Ваш код слегка изменяет семантику: в исходном коде LongRunningTask был выполнен из контекста пула потоков, а в вашем коде он выполнен из любого контекста CallThrottledTasks. Кроме того, ваш код не будет распространять исключения из LongRunningTask чисто; Task<T>.Result обертывает исключения в AggregateException, а await не будет делать никаких оберток.

+0

спасибо. Это точно, что я ищу: побочные эффекты реализации, которые проскальзывают через трещины. Похоже, что есть много чего с асинхронным ожиданием. Я буду работать над обработкой исключений. Я знаком с AggregateException от TPL, но с этим недостаточно справился. – AFM

+2

Это на самом деле больше случай остатков от TPL, которые - очень редко - полезны с 'async', но чаще, чем просто мешают. Например, конструктор 'Task',' Start', 'Wait',' Result', 'ContinueWith',' WaitAll' и 'WaitAny' предназначены для * параллельного * (не * асинхронного *) программирования, и его следует избегать в мир 'async', если вы действительно не знаете, что делаете. –

Смежные вопросы