Jeśli nie chcesz używać async / await w swojej metodzie, ale nadal „dekorujesz” ją, aby móc użyć słowa kluczowego „czekaj” z zewnątrz, TaskCompletionSource.cs :
public static Task<T> RunAsync<T>(Func<T> function)
{
if (function == null) throw new ArgumentNullException(“function”);
var tcs = new TaskCompletionSource<T>();
ThreadPool.QueueUserWorkItem(_ =>
{
try
{
T result = function();
tcs.SetResult(result);
}
catch(Exception exc) { tcs.SetException(exc); }
});
return tcs.Task;
}
Stąd i tutaj
Aby wesprzeć taki paradygmat w zadaniach, potrzebujemy sposobu na zachowanie fasady zadania i możliwość odwołania się do arbitralnej operacji asynchronicznej jako zadania, ale do kontrolowania czasu życia tego zadania zgodnie z regułami podstawowej infrastruktury, która zapewnia asynchronię, i robienie tego w sposób, który nie kosztuje znacząco. To jest cel TaskCompletionSource.
Widziałem, że jest również używany w źródle .NET np. WebClient.cs :
[HostProtection(ExternalThreading = true)]
[ComVisible(false)]
public Task<string> UploadStringTaskAsync(Uri address, string method, string data)
{
// Create the task to be returned
var tcs = new TaskCompletionSource<string>(address);
// Setup the callback event handler
UploadStringCompletedEventHandler handler = null;
handler = (sender, e) => HandleCompletion(tcs, e, (args) => args.Result, handler, (webClient, completion) => webClient.UploadStringCompleted -= completion);
this.UploadStringCompleted += handler;
// Start the async operation.
try { this.UploadStringAsync(address, method, data, tcs); }
catch
{
this.UploadStringCompleted -= handler;
throw;
}
// Return the task that represents the async operation
return tcs.Task;
}
Wreszcie znalazłem przydatne również następujące:
Cały czas zadaje mi to pytanie. Sugeruje to, że musi istnieć jakiś wątek, który blokuje wywołanie We / Wy do zasobu zewnętrznego. A zatem kod asynchroniczny uwalnia wątek żądania, ale tylko kosztem innego wątku w innym miejscu w systemie, prawda? Nie, wcale nie. Aby zrozumieć, dlaczego skalowane są żądania asynchroniczne, prześledzę (uproszczony) przykład asynchronicznego wywołania We / Wy. Powiedzmy, że żądanie musi zostać zapisane do pliku. Wątek żądania wywołuje metodę zapisu asynchronicznego. WriteAsync jest implementowany przez bibliotekę Base Class Library (BCL) i używa portów zakończenia dla swoich asynchronicznych operacji we / wy. Tak więc wywołanie WriteAsync jest przekazywane do systemu operacyjnego jako asynchroniczny zapis pliku. System operacyjny komunikuje się następnie ze stosem sterowników, przekazując dane w celu zapisania w pakiecie żądania we / wy (IRP). Tutaj rzeczy stają się interesujące: Jeśli sterownik urządzenia nie może natychmiast obsłużyć IRP, musi obsłużyć go asynchronicznie. Tak więc sterownik każe dyskowi rozpocząć zapisywanie i zwraca odpowiedź „oczekującą” do systemu operacyjnego. System operacyjny przekazuje tę „oczekującą” odpowiedź do BCL, a BCL zwraca niekompletne zadanie do kodu obsługi żądań. Kod obsługi żądań oczekuje na zadanie, które zwraca niepełne zadanie z tej metody i tak dalej. Na koniec kod obsługi żądań kończy zwracanie niekompletnego zadania do ASP.NET, a wątek żądania może powrócić do puli wątków. Kod obsługi żądań oczekuje na zadanie, które zwraca niepełne zadanie z tej metody i tak dalej. Na koniec kod obsługi żądań kończy zwracanie niekompletnego zadania do ASP.NET, a wątek żądania może powrócić do puli wątków. Kod obsługi żądań oczekuje na zadanie, które zwraca niepełne zadanie z tej metody i tak dalej. Na koniec kod obsługi żądań kończy zwracanie niekompletnego zadania do ASP.NET, a wątek żądania może powrócić do puli wątków.
Wprowadzenie do Async / Await na ASP.NET
Jeśli celem jest poprawa skalowalności (a nie responsywności), wszystko zależy od istnienia zewnętrznego wejścia / wyjścia, który daje taką możliwość.