Mam metodę w aplikacji ASP.NET, której wykonanie zajmuje dużo czasu. Wywołanie tej metody może wystąpić do 3 razy podczas jednego żądania użytkownika, w zależności od stanu pamięci podręcznej i parametrów podanych przez użytkownika. Każde połączenie trwa około 1-2 sekund. Sama metoda jest synchronicznym wywołaniem usługi i nie ma możliwości nadpisania implementacji.
Więc synchroniczne wywołanie usługi wygląda mniej więcej tak:
public OutputModel Calculate(InputModel input)
{
// do some stuff
return Service.LongRunningCall(input);
}
A użycie metody to (zauważ, że wywołanie metody może się zdarzyć więcej niż raz):
private void MakeRequest()
{
// a lot of other stuff: preparing requests, sending/processing other requests, etc.
var myOutput = Calculate(myInput);
// stuff again
}
Próbowałem zmienić implementację z mojej strony, aby zapewnić jednoczesną pracę tej metody i oto do czego doszedłem do tej pory.
public async Task<OutputModel> CalculateAsync(InputModel input)
{
return await Task.Run(() =>
{
return Calculate(input);
});
}
Użycie (część kodu „do innych rzeczy” działa jednocześnie z wywołaniem usługi):
private async Task MakeRequest()
{
// do some stuff
var task = CalculateAsync(myInput);
// do other stuff
var myOutput = await task;
// some more stuff
}
Moje pytanie jest następujące. Czy używam właściwego podejścia, aby przyspieszyć wykonywanie w aplikacji ASP.NET, czy też wykonuję niepotrzebne zadanie, próbując uruchomić kod synchroniczny asynchronicznie? Czy ktoś może wyjaśnić, dlaczego drugie podejście nie jest opcją w ASP.NET (jeśli tak naprawdę nie jest)? Ponadto, jeśli takie podejście jest możliwe, czy muszę wywoływać taką metodę asynchronicznie, jeśli jest to jedyne wywołanie, jakie możemy w tej chwili wykonać (mam taki przypadek, gdy nie ma innych rzeczy do zrobienia w oczekiwaniu na zakończenie)?
Większość artykułów w sieci na ten temat dotyczy użycia async-await
podejścia z kodem, który już dostarcza awaitable
metod, ale to nie mój przypadek. Tutajto fajny artykuł opisujący mój przypadek, który nie opisuje sytuacji połączeń równoległych, rezygnując z opcji zawijania wywołania synchronizacji, ale moim zdaniem moja sytuacja jest właśnie do tego okazją.
Z góry dziękuję za pomoc i wskazówki.
źródło
Task.Run
, zawartość jest uruchamiana w innym wątku, który jest pobierany z puli wątków. Wtedy nie ma potrzeby zawijania wywołań blokujących (takich jak moje wywołanie usługi) wRun
s, ponieważ każdy z nich będzie zawsze zużywał jeden wątek, który zostanie zablokowany podczas wykonywania metody. W takiej sytuacji jedyną korzyścią, jakaasync-await
mi pozostała, jest wykonywanie kilku czynności na raz. Proszę popraw mnie jeżeli się mylę.Run
(lubParallel
), jest współbieżność. Każda operacja nadal blokuje wątek. Ponieważ twierdzisz, że usługa jest usługą sieciową, nie polecam używaniaRun
lubParallel
; Zamiast tego napisz asynchroniczny interfejs API dla usługi.BeginGetResponse
i rozpocząć kilka z nich, a następnie (synchronicznie) blok aż wszystkie z nich są kompletne, ale takie podejście jest trudne - zobacz blog.stephencleary.com/2012/07/dont-block-on -async-code.html i msdn.microsoft.com/en-us/magazine/mt238404.aspx . Jeśli to możliwe, zwykle łatwiej i czystsze jest przyjęcieasync
całej metody.Zauważyłem, że poniższy kod może przekonwertować Task, aby zawsze działał asynchronicznie
private static async Task<T> ForceAsync<T>(Func<Task<T>> func) { await Task.Yield(); return await func(); }
i użyłem go w następujący sposób
await ForceAsync(() => AsyncTaskWithNoAwaits())
Spowoduje to asynchroniczne wykonanie dowolnego zadania, dzięki czemu można je połączyć w scenariuszach WhenAll, WhenAny i innych zastosowaniach.
Możesz także po prostu dodać Task.Yield () jako pierwszą linię wywoływanego kodu.
źródło