2016-07-26 3 views
-7

Ich implementiere sowohl asynchrone als auch normale Versionen einer Funktion. Ich habe bereits eine Async-Version implementiert. Für die normale Version kann ich die Verwendung von asynchronen Funktionsaufrufen mit ihren normalen Gegenstücken kopieren, einfügen und ersetzen. Oder ich kann die Async-Version mit Result wie folgt aufrufen.Implementieren von Funktion mit Async-Version mit Ergebnis

Erster Ansatz:

public int SomeTask(int param) 
{ 
    //Something going on 
    return SomeOtherTask(); 
} 

public async Task<int> SomeTaskAsync(int param) 
{ 
    //Something going on (copy pasted) 
    return await SomeOtherTaskAsync();  
} 

Zweiter Ansatz:

public int SomeTask(int param) 
{ 
    return SomeTaskAsync(param).Result; 
} 

public async Task<int> SomeTaskAsync(int param) 
{ 
    //some function calls with await 
} 

Gibt es ein mögliches Problem mit dem zweiten Ansatz?

+3

Paar Artikel, die Sie lesen möchten - [? Soll ich Asynchronous Wrappers belichten für synchrone Methoden] (http://blogs.msdn.com/b/pfxteam/ archiv/2012/03/24/10287244.aspx) und [Sollte ich synchrone Wrapper für asynchrone Methoden verfügbar machen?] (http://blogs.msdn.com/b/pfxteam/archive/2012/04/13/10293638.aspx). Die Antwort in beiden Artikeln ist im Allgemeinen nein. –

Antwort

2

Die Leistung wird beeinträchtigt. Wie viel ist unmöglich zu sagen - Sie sind derjenige mit dem Code, können Sie das Profiling für Ihren genauen Anwendungsfall tun.

Es macht jedoch keinen Sinn, eine Synchronisationsversion der Methode zu erstellen, die nur Result aufruft - der Benutzer Ihrer Methode kann das Gleiche tun. Das Problem ist, dass es ziemlich gefährlich sein kann, das zu tun, besonders wenn Synchronisationskontexte involviert sind. Betrachten Sie den Beispielcode:

async void btnTest_Click(object sender, EventArgs e) 
{ 
    await DoSomethingAsync(); 
} 

async Task DoSomethingAsync() 
{ 
    await Task.Delay(1000); 
} 

Das funktioniert gut. Jetzt versuchen wir Ihre "Sync" Version:

async void btnTest_Click(object sender, EventArgs e) 
{ 
    DoSomethingAsync().Result; 
} 

async Task DoSomethingAsync() 
{ 
    await Task.Delay(1000); 
} 

Hoppla, Sie haben einen Deadlock. Der UI-Thread wartet auf DoSomethingAsync, um fertig zu sein, aber DoSomethingAsync muss die Ausführung auf dem UI-Thread beenden. Sie können niemals davon ausgehen, dass eine async-Methode ausgeführt wird, wenn Sie synchron darauf warten.

Durch die Verwendung von Result anstelle von await verlieren Sie eine Menge der Ausnahmebehandlungsfunktionen. Zum Beispiel wird der Ausnahme-Stack-Trace komplett durcheinander gebracht, und Sie müssen Ausnahmen behandeln, die sowohl von der Methode, die den Task erstellt, als auch vom Result-Aufruf selbst ausgelöst werden - der erste löst Ausnahmen bis zu dem Punkt des ersten aus await das muss eigentlich warten, und das zweite für alle Fortsetzungen. Du weißt nie, welches was ist.

0

Erstens, wenn Sie eine natürlich-asynchrone Operation haben, sollten Sie eine asynchrone API verfügbar machen. Wie Damien sagte, ist die Antwort auf "Should I expose synchronous wrappers for my asynchronous methods?" "Nein".

Ein Problem mit diesen Arten von Wrappern ist, dass gibt es kein Muster, das in allen Szenarien funktioniert! Ich beschreibe eine Vielzahl von Sync-über-Async-Hacks in meinem Artikel unter Brownfield Async. Jeder von ihnen hat Nachteile; Wie Luaan sagte, hat der blockierende Hack die Möglichkeit von Deadlocks.

Wenn Sie jedoch einen wirklich guten Grund dafür haben (dh Ihre Bibliothek hatte in der Vergangenheit synchrone Methoden, und Sie fügen asynchrone Methoden hinzu, aber wollen die synchronen Methoden für Abwärtskompatibilität, zumindest für eine Version oder zwei), dann können Sie den "boolean flag hack" verwenden, wie in meinem Artikel beschrieben.

Stephen Toub zeigte mir diesen Trick vor einer Weile. Die Idee ist, dass die (private) Implementierungsfunktion einen bool sync Parameter annimmt, und wenn das true ist, dann ist die Aufgabe, die sie zurückgibt, garantiert bereits abgeschlossen.Dies vermeidet die Deadlock Probleme mit regelmäßigen Blockierung:

private async Task<int> SomeTaskAsync(int param, bool sync) 
{ 
    // Every `await` in your code needs to honor `sync` 
    if (sync) 
    return SomeOtherTask(); 
    else 
    return await SomeOtherTaskAsync(); 
} 

public int SomeTask(int param) 
{ 
    return SomeTaskAsync(param, sync: true).GetAwaiter().GetResult(); 
} 

public Task<int> SomeTaskAsync(int param) 
{ 
    return SomeTaskAsync(param, sync: false); 
}