Wenn Sie async / await nicht in Ihrer Methode verwenden möchten, es aber dennoch "dekorieren" möchten, um das Schlüsselwort await von außen verwenden zu können, 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;
}
Von hier und hier
Um ein solches Paradigma mit Aufgaben zu unterstützen, benötigen wir eine Möglichkeit, die Aufgabenfassade beizubehalten und eine beliebige asynchrone Operation als Aufgabe zu bezeichnen, aber die Lebensdauer dieser Aufgabe gemäß den Regeln der zugrunde liegenden Infrastruktur zu steuern, die die Aufgabe bereitstellt Asynchronität, und dies auf eine Weise, die nicht wesentlich kostet. Dies ist der Zweck von TaskCompletionSource.
Ich habe gesehen, dass es auch in der .NET-Quelle verwendet wird, z. 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;
}
Schließlich fand ich auch folgendes nützlich:
Diese Frage wird mir die ganze Zeit gestellt. Die Implikation ist, dass irgendwo ein Thread vorhanden sein muss, der den E / A-Aufruf an die externe Ressource blockiert. Asynchroner Code gibt also den Anforderungsthread frei, aber nur auf Kosten eines anderen Threads an einer anderen Stelle im System, oder? Nein überhaupt nicht. Um zu verstehen, warum asynchrone Anforderungen skaliert werden, werde ich ein (vereinfachtes) Beispiel für einen asynchronen E / A-Aufruf verfolgen. Angenommen, eine Anforderung muss in eine Datei geschrieben werden. Der Anforderungsthread ruft die asynchrone Schreibmethode auf. WriteAsync wird von der Base Class Library (BCL) implementiert und verwendet Abschlussports für die asynchrone E / A. Daher wird der WriteAsync-Aufruf als asynchroner Dateischreibvorgang an das Betriebssystem weitergegeben. Das Betriebssystem kommuniziert dann mit dem Treiberstapel und leitet die Daten weiter, um sie in ein E / A-Anforderungspaket (IRP) zu schreiben. Hier wird es interessant: Wenn ein Gerätetreiber ein IRP nicht sofort verarbeiten kann, muss er es asynchron verarbeiten. Der Treiber weist die Festplatte an, mit dem Schreiben zu beginnen, und gibt eine ausstehende Antwort an das Betriebssystem zurück. Das Betriebssystem übergibt diese "ausstehende" Antwort an die BCL, und die BCL gibt eine unvollständige Aufgabe an den Anforderungsbearbeitungscode zurück. Der Anforderungsbearbeitungscode wartet auf die Aufgabe, die eine unvollständige Aufgabe von dieser Methode usw. zurückgibt. Schließlich gibt der Anforderungsbearbeitungscode eine unvollständige Aufgabe an ASP.NET zurück, und der Anforderungsthread wird freigegeben, um zum Threadpool zurückzukehren. Der Anforderungsbearbeitungscode wartet auf die Aufgabe, die eine unvollständige Aufgabe von dieser Methode usw. zurückgibt. Schließlich gibt der Anforderungsbearbeitungscode eine unvollständige Aufgabe an ASP.NET zurück, und der Anforderungsthread wird freigegeben, um zum Threadpool zurückzukehren. Der Anforderungsbearbeitungscode wartet auf die Aufgabe, die eine unvollständige Aufgabe von dieser Methode usw. zurückgibt. Schließlich gibt der Anforderungsbearbeitungscode eine unvollständige Aufgabe an ASP.NET zurück, und der Anforderungsthread wird freigegeben, um zum Threadpool zurückzukehren.
Einführung in Async / Await unter ASP.NET
Wenn das Ziel darin besteht, die Skalierbarkeit (und nicht die Reaktionsfähigkeit) zu verbessern, hängt alles von der Existenz einer externen E / A ab, die die Möglichkeit dazu bietet.