2014-01-12 3 views
6

У меня есть контроллер async asp.net. Этот контроллер вызывает метод async. Метод, который фактически выполняет работу async IO, глубоко в моем приложении. Ряд методов между контроллером и последним методом в цепочке отмечены асинхронным модификатором. Вот пример того, как у меня есть установки коды:async all the way down issue

public async Task<ActionResult> Index(int[] ids) 
{ 
    List<int> listOfDataPoints = dataPointService(ids); 
    List<Task> dpTaskList = new List<Task>(); 
    foreach (var x in listOfDataPoints) 
    { 
     dpTaskList.Add(C_Async(x)); 
    } 

    await Task.WhenAll(dpTaskList); 
    return View(); 
} 


private async Task C_Async(int id) 
{ 
    //this method executes very fast 
    var idTemp = paddID(id); 
    await D_Async(idTemp); 
} 

private async Task D_Async(string id) 
{ 
    //this method executes very fast 
    await E_Async(id); 
} 

private async Task E_Async(string url) 
{ 
    //this method performs the actual async IO 
    result = await new WebClient().DownloadStringTaskAsync(new Uri(url)) 
    saveContent(result); 
} 

Как вы можете видеть, что контроллер вызывает C_Async (х) асинхронна, то есть цепь асинхронных методов к E_Async. Существуют методы между контроллером и E_Async, и все они имеют модификатор async. Есть ли штраф за производительность, поскольку существуют методы, использующие асинхронный модификатор, но не выполняющий работу с асинхронным IO?

Примечание: Это упрощенная версия реального кода, существует более асинхронных методов между контроллером и методом E_Async.

+0

Возможный дубликат [Async all the down down?] (Http: // stackoverflow.com/questions/12016322/async-all-the-way-down) –

+0

Конечно, есть накладные расходы. Но ваш вопрос ведет неверный путь. Если вызываемые задачи быстрые, вы можете их синхронно вызывать без async. Если вы хотите быть уверенным: Измерьте. –

+0

@Alois - Точно, как бы я вызывал методы синхронно при вызове последнего метода асинхронно? Я новичок в асинхронном программировании, поэтому не стесняйтесь переписывать мой пост. – Luke101

ответ

4

Да. Существует штраф (хотя и не огромный), а если вы не нужно быть async не быть. Этот шаблон часто называют «return wait», где вы почти всегда можете удалить как async, так и await. Просто возвратите задачу у вас уже есть, что представляет асинхронные операции:

private Task C_Async(int id) 
{ 
    // This method executes very fast 
    var idTemp = paddID(id); 
    return D_Async(idTemp); 
} 

private Task D_Async(string id) 
{ 
    // This method executes very fast 
    return E_Async(id); 
} 

В данном конкретном случае Index только await задачи, которые E_Async возвратов. Это означает, что после завершения всего I/O следующей строкой кода будет непосредственно return View();. C_Async и D_Async уже запущены и закончили синхронный вызов.

+2

Это хороший ответ, но есть еще одна важная деталь: модификатор 'async' также будет захватывать исключения из синхронной части метода и помещать их в возвращаемую' Task', в то время как методы non-'async' будут вызывать исключения непосредственно. –

2

Вы должны быть осторожны с насосами сообщений нитей и тем, что действительно делает асинхронный процесс. В приведенном ниже примере вызывается метод async, который вызывает два других метода async, которые запускают две задачи для выполнения фактической работы, которая ждет 2 и 3 секунды.

13.00 6520 .ctor Calling async method 
13.00 6520 RunSomethingAsync Before 
13.00 6520 GetSlowString Before 
13.00 5628 OtherTask Sleeping for 2s 
15.00 5628 OtherTask Sleeping done 
15.00 6520 GetVerySlow Inside 
15.00 2176 GetVerySlow Sleeping 3s 
18.00 2176 GetVerySlow Sleeping Done 
18.00 6520 RunSomethingAsync After GetSlowOtherTaskResultGetVerySlowReturn 

Как вы можете видеть, звонки сериализуются, что может не быть тем, что вы хотите, когда вы после выполнения. Возможно, два разных вызова ожидания не зависят друг от друга и могут быть запущены напрямую в качестве задач.

Все методы до тех пор, пока GetSlowStringBefore не будут вызваны в потоке пользовательского интерфейса или ASP.NET, который запустил операцию async (если у него есть насос сообщений). Только последний вызов с результатом операции сортируется обратно к инициирующему потоку.

Предел производительности - это где-то в области ContextSwitch, чтобы разбудить уже существующий поток. Это должно быть где-то на уровне микросекунды. Самым дорогим материалом было бы создание управляемых объектов и сборщик мусора, очищающий временные объекты. Если вы вызовете это в узком цикле, вы будете связаны GC, потому что существует верхний предел, сколько потоков может быть создано. В этом случае TPL будет буферизовать ваши задачи в очередях, которые требуют выделения памяти, а затем сбрасывать очереди с n рабочих потоков из пула потоков.

На моем ядре I7 я получаю накладные расходы на 2микросекунды для каждого вызова (закомментируйте строку Debug.Print) и потребление памяти 6,5 ГБ для 5 миллионов вызовов в приложении WPF, что дает вам накладные расходы в 130 КБ за асинхронную цепочку операций. Если вы после высокой масштабируемости, вам нужно следить за GC. До Joe Duffy has finished his new language мы должны использовать CLR, который у нас есть.

public partial class MainWindow : Window 
{ 
    public MainWindow() 
    { 
     InitializeComponent(); 
     Print("Calling async method"); 
     RunSomethingAsync(); 
    } 

    private async void RunSomethingAsync() 
    { 
     Print("Before"); 
     string msg = await GetSlowString(); 
     Print("After " + msg); 
     cLabel.Content = msg; 
    } 

    void Print(string message, [CallerMemberName] string method = "") 
    { 
     Debug.Print("{0:N2} {1} {2} {3}", DateTime.Now.Second, AppDomain.GetCurrentThreadId(), method, message); 
    } 

    private async Task<string> GetSlowString() 
    { 
     Print("Before"); 

     string otherResult = await OtherTask(); 

     return "GetSlow" + otherResult + await GetVerySlow(); ; 
    } 

    private Task<string> OtherTask() 
    { 
     return Task.Run(() => 
     { 
      Print("Sleeping for 2s"); 
      Thread.Sleep(2 * 1000); 
      Print("Sleeping done"); 
      return "OtherTaskResult"; 
     }); 
    } 

    private Task<string> GetVerySlow() 
    { 
     Print("Inside"); 
     return Task.Run(() => 
     { 
      Print("Sleeping 3s"); 
      Thread.Sleep(3000); 
      Print("Sleeping Done"); 
      return "GetVerySlowReturn"; 
     }); 
    } 
} 
+0

@downvoter: Заметь прокомментировать? –

+0

Мне тоже было бы интересно узнать. То, о чем вы говорите, имеет смысл. Возможно, никому не понравилось, что вы ссылаетесь на поток сообщений и WPF, в то время как в вопросе OP упоминается ASP.NET? – Noseratio

+0

Если WPF или ASP.NET используется для отображения async не имеет значения, если поток имеет набор SynchronizationContext, к которому результат может быть отправлен обратно. Но это, безусловно, может смутить людей. –