2016-06-07 11 views
3

Ich würde sagen, die folgenden zwei Code-Schnipsel, die ich habe, sind gleichwertig, aber sie sind nicht.Extension-Methode entspricht nicht direkt Anruf

Die folgende korrekt arbeitet:

var entry3 = Task.Run(async() => await entry2.GetMemberGroupsAsync(false)).WaitForResult().FirstOrDefault(); 

Der folgende Code, wo ich gerade bewegt die Task.Run.WaitForResult Kette in einem Verlängerungsverfahren, nicht funktioniert, sondern erzeugt einen Deadlock:

var entry3 = entry2.GetMemberGroupsAsync(false).RunSynchronouslyAndReturnResult().FirstOrDefault(); 

public static T RunSynchronouslyAndReturnResult<T>(this Task<T> task) 
{ 
    return Task.Run(async() => await task).WaitForResult(); 
} 

Warum sind diese beiden Codefragmente nicht gleichwertig?

Der Vollständigkeit halber wird die Methode GetMemberGroupsAsync von Microsoft Azure Graph API zur Verfügung gestellt, und die Funktion WaitForResult ist unten definiert. Soweit ich sehen kann, macht es nichts anderes abhängig von dem Anrufernamen oder etw. dass, wie:

public static TResult WaitForResult<TResult>(this Task<TResult> task, 
              bool continueOnCapturedContext = false) 
{ 
    if (task == null) 
    { 
     throw new ArgumentNullException("task"); 
    } 

    try 
    { 
     return PreventForDeadLocks(task, continueOnCapturedContext).Result; 
    } 
    catch (AggregateException ex) 
    { 
     if (ex.InnerExceptions.Count == 1) 
     { 
      throw ex.InnerExceptions[0]; 
     } 

     throw; 
    } 
} 

public static async Task<TResult> PreventForDeadLocks<TResult>(this Task<TResult> task, 
                   bool continueOnCapturedContext = false) 
{ 
    return await task.ConfigureAwait(continueOnCapturedContext: continueOnCapturedContext); 
} 
+1

Noch schlimmer, 'PreventForDeadlocks' verhindert keine Deadlocks. ** Es gibt kein Sync-über-Async-Muster, das in allen Situationen funktioniert! ** –

+0

Warum nicht einfach 'var entry3 = (warten Sie auf entry2.GetMemberGroupsAsync (false)). FirstOrDefault();'? Liegt es daran, dass Sie die Methode, die diesen Code enthält, in "async" machen müssen? In der Regel lässt man das 'async'-'await'-Muster bis zu den Ereignissen durchblubbern und macht dann diese' async'-'away' (fire and forget). – juharr

+0

@juharr nur, dass ich keine Ereignisse habe - der Code ist in einem WebAPI-Endpunkt. Kann ich diesen WebAPI-Endpunkt asynchron machen? – Alexander

Antwort

3

Der Unterschied besteht darin, in welchem ​​Synchronisationskontext Ihre Aufgabe gestartet wurde. Hier:

var entry3 = Task.Run(async() => await entry2.GetMemberGroupsAsync(false)).WaitForResult().FirstOrDefault(); 

Sie Ihre async Aufgabe starten (ich meine await entry2.GetMemberGroupsAsync(false)) innerhalb Task.Run Anruf, so UI Synchronisationskontext nicht erfasst. Aber hier:

var entry3 = entry2.GetMemberGroupsAsync(false).RunSynchronouslyAndReturnResult().FirstOrDefault(); 

Sie implizit Ihre Aufgabe starten (entry2.GetMemberGroupsAsync(false) kehrt Aufgabe) auf UI-Kontext, so UI Synchronisationskontext erfasst, und Sie haben Ihre Deadlock.

3

Im ersten Fall wird GetMemberGroupsAsync auf einem anderen Thread als WaitForResult genannt.

Im zweiten Fall wird es im selben Thread wie WaitForResult aufgerufen. Sie sind nur warten auf auf einem anderen Thread.

+0

Warum ist das so? wird die Extension-Methode in einem anderen Thread ausgeführt? – Alexander

+0

Im ersten Fall wird GetMemberGroupsAsync innerhalb der Task.Run aufgerufen, sodass es in einem anderen Thread ausgeführt wird. Im zweiten Fall wird GetMemberGroupsAsync zuerst aufgerufen (der Task wurde also bereits gestartet) und die resultierende Task wird dann an Task.Run übergeben. Im zweiten Fall wird GetMemberGroupsAsync _nicht_ in einem anderen Thread aufgerufen. Macht Sinn? –