У меня есть метод в приложении ASP.NET, на выполнение которого уходит довольно много времени. Вызов этого метода может происходить до 3 раз в течение одного пользовательского запроса, в зависимости от состояния кеша и параметров, которые пользователь предоставляет. Каждый вызов занимает около 1-2 секунд. Сам метод является синхронным вызовом службы и нет возможности переопределить реализацию.
Таким образом, синхронный вызов службы выглядит примерно так:
public OutputModel Calculate(InputModel input)
{
// do some stuff
return Service.LongRunningCall(input);
}
И использование метода (обратите внимание, что вызов метода может происходить более одного раза):
private void MakeRequest()
{
// a lot of other stuff: preparing requests, sending/processing other requests, etc.
var myOutput = Calculate(myInput);
// stuff again
}
Я пытался изменить реализацию со своей стороны, чтобы обеспечить одновременную работу этого метода, и вот к чему я до сих пор пришел.
public async Task<OutputModel> CalculateAsync(InputModel input)
{
return await Task.Run(() =>
{
return Calculate(input);
});
}
Использование (часть кода "делать другое" выполняется одновременно с вызовом службы):
private async Task MakeRequest()
{
// do some stuff
var task = CalculateAsync(myInput);
// do other stuff
var myOutput = await task;
// some more stuff
}
У меня следующий вопрос. Правильно ли я использую для ускорения выполнения в приложении ASP.NET или выполняю ненужную работу, пытаясь запустить синхронный код в асинхронном режиме? Может ли кто-нибудь объяснить, почему второй подход не подходит для ASP.NET (если это действительно не так)? Кроме того, если такой подход применим, нужно ли мне вызывать такой метод асинхронно, если это единственный вызов, который мы могли бы выполнить в данный момент (у меня есть такой случай, когда больше ничего не нужно делать в ожидании завершения)?
Большинство статей в сети по этой теме посвящены использованию async-await
подхода с кодом, который уже предоставляет awaitable
методы, но это не мой случай. Вот- это хорошая статья, описывающая мой случай, в которой не описывается ситуация с параллельными вызовами, отказываясь от возможности обернуть вызов синхронизации, но, на мой взгляд, моя ситуация как раз является поводом для этого.
Заранее благодарим за помощь и советы.
Task.Run
, содержимое запускается в другом потоке, который берется из пула потоков. Тогда вообще нет необходимости переносить блокирующие вызовы (например, мой вызов службы) вRun
s, потому что они всегда будут использовать один поток каждый, который будет заблокирован во время выполнения метода. В такой ситуации единственное преимущество, которое остаетсяasync-await
в моем случае, - это одновременное выполнение нескольких действий. Пожалуйста, поправьте меня, если я ошибаюсь.Run
(илиParallel
), - это параллелизм. Каждая операция по-прежнему блокирует поток. Поскольку вы говорите, что служба является веб-службой, я не рекомендую использоватьRun
илиParallel
; вместо этого напишите асинхронный API для службы.BeginGetResponse
запустить несколько из них, а затем (синхронно) блокировать, пока все они не будут завершены, но такой подход сложен - см. Blog.stephencleary.com/2012/07/dont-block-on -async-code.html и msdn.microsoft.com/en-us/magazine/mt238404.aspx . Как правило, по возможности проще и чище внедритьasync
полностью.Я обнаружил, что следующий код может преобразовать задачу, чтобы она всегда выполнялась асинхронно.
private static async Task<T> ForceAsync<T>(Func<Task<T>> func) { await Task.Yield(); return await func(); }
и я использовал его следующим образом
await ForceAsync(() => AsyncTaskWithNoAwaits())
Это будет выполнять любую задачу асинхронно, поэтому вы можете комбинировать их в сценариях WhenAll, WhenAny и других целях.
Вы также можете просто добавить Task.Yield () в качестве первой строки вызываемого кода.
источник