2017-12-18 3 views
2

erwartete ich the following code haben, die auf .NET Standard-2.0 ausgeführt wird:Async Await und ContinueWith funktioniert nicht wie

public static Task<JobResult> TryRunAsync(this IJob job, 
              CancellationToken cancellationToken = default(CancellationToken)) 
{ 
    return job.RunAsync(cancellationToken) 
     .ContinueWith(t => { 
      if (t.IsFaulted) 
       return JobResult.FromException(t.Exception.InnerException); 
      if (t.IsCanceled) 
       return JobResult.Cancelled; 

      return t.Result; 
     }); 
} 

Und wir bemerkt, es war nicht wie erwartet ausgeführt wird. Wir dachten, dass, wenn Sie den Aufruf von TryRun erwarteten, es immer die Fortsetzung aufrufen würde, die die Ausnahme/Löschung behandeln und ein Job-Ergebnis zurückgeben könnte. Wir hatten gehofft, die Menge an asynchronen Zustandsmaschinen zu reduzieren ... Das ist jedoch nicht der Fall, es explodiert einfach. Hier ist eine kleinere Probe (eine neue .net Kern 2.0 Konsole app erstellen und fügen Sie den folgenden:.

using System; 
using System.Threading.Tasks; 

namespace ConsoleApp4 
{ 
    public class Program 
    { 
     public static async Task Main() 
     { 
      // works 
      await DoStuff(); 
      Console.ReadKey(); 

      // blows up 
      await TryRun(); 
      Console.ReadKey(); 
     } 

     public static Task DoStuff() 
     { 
      return Method() 
       .ContinueWith(t => Throws()) 
       .ContinueWith(t => { 
        if (t.IsFaulted) 
         Console.WriteLine("Faulted"); 
        else if (t.IsCompletedSuccessfully) 
         Console.WriteLine("Success"); 
       }); 
     } 

     public static Task Method() 
     { 
      Console.WriteLine("Method"); 
      return Task.CompletedTask; 
     } 

     public static Task TryRun() 
     { 
      return Throws() 
       .ContinueWith(t => { 
        if (t.IsFaulted) 
         Console.WriteLine("Faulted"); 
        else if (t.IsCompletedSuccessfully) 
         Console.WriteLine("Success"); 
       }); 
     } 

     public static Task Throws() 
     { 
      Console.WriteLine("Throws"); 
      throw new ApplicationException("Grr"); 
     } 
    } 
} 

Sie müssen möglicherweise <LangVersion>Latest</LangVersion> In Ihrem csproj

UPDATE

Am Ende haben wir gehen mit dem folgenden Code:

public static Task<JobResult> TryRunAsync(this IJob job, 
              CancellationToken cancellationToken = default(CancellationToken)) 
{ 
    var tcs = new TaskCompletionSource<JobResult>(null); 
    try { 
     var task = job.RunAsync(cancellationToken); 
     task.ContinueWith((task2, state2) => { 
      var tcs2 = (TaskCompletionSource<object>)state2; 
      if (task2.IsCanceled) { 
       tcs2.SetResult(JobResult.Cancelled); 
      } else if (task2.IsFaulted) { 
       tcs2.SetResult(JobResult.FromException(task2.Exception)); 
      } else { 
       tcs2.SetResult(JobResult.Success); 
      } 
     }, tcs, cancellationToken); 
    } catch (Exception ex) { 
     tcs.SetResult(JobResult.FromException(ex)); 
    } 

    return tcs.Task; 
} 

Antwort

5

Die Methode wirft wirft tatsächlich eine Ausnahme, wenn sie aufgerufen wird, nicht r einen fehlerhaften Task. Es gibt keine Task für Sie, um eine Fortsetzung hinzuzufügen; Es geht einfach den Call-Stack hinauf, bevor der Call ContinueWith erreicht wird.

+0

Kommentare sind nicht für längere Diskussion; Diese Konversation wurde [in den Chat verschoben] (http://chat.stackoverflow.com/rooms/161464/discussion-on-answer-by-servy-async-mait-and-continuewith-not-working-as-expect) . – Andy

0

Tatsächlich wird hier keine Aufgabe erstellt. Zum Beispiel, wenn Sie eine Schleife gemacht haben, würden Sie auf demselben Thread und demselben Stapel bleiben. Sie können das richtige Verhalten sehen, indem Sie eine Task.FromException in der Throws-Methode ausführen und nicht werfen. Auch im Kern 2.1 zumindest können Sie feststellen, dass eine asynchrone Methode genauso schnell oder sogar schneller als die Fortsetzungsversion und weniger Zuteilung sein wird. Es lohnt sich, die Ablaufverfolgungsnummern zu überprüfen, bevor Sie versuchen, die Zustandsmaschine zu optimieren. Auch wenn Sie Ausnahmen werfen, ist Ihre State Machine definitiv die geringste Ihrer Sorgen.