Der beste Weg, um eine auf Rückrufen basierende asynchrone Methode in eine erwartete Aufgabe zu konvertieren

77

Was wäre der beste Weg, um eine "klassische" asynchrone Methode zu konvertieren / zu verpacken, die einen Rückruf auf etwas verwendet, das eine (erwartete) Aufgabe zurückgibt?

Zum Beispiel mit der folgenden Methode:

public void GetStringFromUrl(string url, Action<string> onCompleted);

Ich weiß nur, wie ich dies in eine Methode einbinden kann, die eine Aufgabe zurückgibt:

public Task<string> GetStringFromUrl(string url)
{
     var t = new TaskCompletionSource<string>();

     GetStringFromUrl(url, s => t.TrySetResult(s));

     return t.Task;
}

Ist dies der einzige Weg, dies zu erreichen?

Und gibt es eine Möglichkeit, den Aufruf von GetStringFromUrl (URL, Rückruf) in der Aufgabe selbst zu verpacken (dh der Aufruf selbst würde innerhalb der Aufgabe statt synchron ausgeführt)?

Philippe Leybaert
quelle
2
Übrigens ist dies keine „klassische“ asynchrone .net-Methode. Das sind BeginXxx()und EndXxx()Paare. Warum suchen Sie nach anderen Möglichkeiten, dies zu tun? Was hoffen Sie zu gewinnen?
Svick
7
Ich möchte nur sicherstellen, dass mir keine offensichtliche Alternative fehlt, um dasselbe zu tun.
Philippe Leybaert
Gibt es einen Grund, TrySetResultstatt SetResulthier zu verwenden?
Ben Huber

Antworten:

41

Ihr Code ist kurz, lesbar und effizient, daher verstehe ich nicht, warum Sie nach Alternativen suchen, aber mir fällt nichts ein. Ich denke, Ihr Ansatz ist vernünftig.

Ich bin mir auch nicht sicher, warum Sie denken, dass der synchrone Teil in der Originalversion in Ordnung ist, aber Sie möchten ihn in der Taskbasierten Version vermeiden . Wenn Sie der Meinung sind, dass der synchrone Teil zu lange dauert, beheben Sie ihn für beide Versionen der Methode.

Wenn Sie es jedoch ThreadPoolnur in der TaskVersion asynchron (dh auf dem ) ausführen möchten, können Sie Folgendes verwenden Task.Run():

public Task<string> GetStringFromUrl(string url)
{
    return Task.Run(() =>
    {
        var t = new TaskCompletionSource<string>();

        GetStringFromUrl(url, s => t.TrySetResult(s));

        return t.Task;
    });
}
svick
quelle
1
Ich kontrolliere nicht immer die vorhandene Callback-basierte Methode. Wenn also der synchrone Teil der Methode etwas tut, das zu lange dauert, möchte ich die Option haben, dies auch in einer Aufgabe zu verschieben. Ihre Lösung löst genau das.
Philippe Leybaert
1
Hmmm ... Ich habe angenommen, dass das ursprüngliche GetStringFromUrl mit dem Rückruf bereits asynchron ist (daher der Rückruf). In diesem Fall werden durch diesen Vorschlag Ressourcen verbrannt, die eine andere Aufgabe auslösen, nur um den Aufruf aufzurufen.
Drew Marsh
@DrewMarsh Selbst asynchrone Methoden haben normalerweise einen synchronen Teil, obwohl dieser meistens vernachlässigbar ist. Aber die Frage, die speziell dafür gestellt wurde, würde ich sonst nicht vorschlagen.
Svick
1
@svick Sicher, es gibt normalerweise einige Startkosten, aber wenn nicht jemand eine wirklich miese Implementierung erstellt hat, würde ich weniger Geld ausgeben, als wenn Sie diesen Teil mit Task.Run auf einen anderen Thread übertragen würden, als nur diesen Teil auf dem ausführen zu lassen aktueller Thread. Ich denke, Sie müssen der asynchronen API, die Sie in dieser Hinsicht aufrufen, irgendwie vertrauen, bis Sie beweisen können, dass sie schuldig ist, und ich persönlich würde niemals empfehlen, dies standardmäßig zu tun. Nur meine 2 ¢.
Drew Marsh
Wenn diese Methode wirklich asynchron ist, verliert das Starten einer neuen Aufgabe alle Vorteile von asynchron ausgeführten Jobs. Wenn Sie möchten, können Sie Task.Yield () vor dem Methodenaufruf aufrufen, um es in den Taskplaner zu stellen. Der Code der Frage wird in jeder Hinsicht besser sein.
Alex Lyalka
5

Ihre angenommene Implementierung ist hierfür vollkommen in Ordnung, vorausgesetzt, der Rückruf behandelt immer nur erfolgreiche Situationen. Was passiert derzeit, wenn eine Ausnahme innerhalb der asynchronen Grundlagen der GetStringFromUrl-Implementierung auftritt? Es gibt keine wirkliche Möglichkeit für sie, dies an den Action-Rückruf weiterzugeben ... schlucken sie es einfach und geben Ihnen null oder so zurück?

Das einzige, was ich empfehlen würde, ist die Befolgung der neuen Konvention, solche asynchronen Methoden mit dem Suffix XXXAsync zu benennen.

Drew Marsh
quelle