2015-03-08 6 views
6

Angenommen, ich habe eine Schnittstelle, die eine asynchrone Methode enthält, und ich habe zwei verschiedene Implementierungen dieser Schnittstelle. Eine der beiden Implementierungen ist natürlich asynchron und die andere nicht. Was wäre der "korrekteste" Weg zur Implementierung der Nicht-Async-Methode?To Task.Run oder nicht zu Task.Run

public interface ISomething { 
    Task<Foo> DoSomethingAsync(); 
} 

// Normal async implementation 
public class Implementation1 : ISomething { 
    async Task<Foo> ISomething.DoSomethingAsync() { 
     return await DoSomethingElseAsync(); 
    } 
} 

// Non-async implementation 
public class Implementation2 : ISomething { 
    // Should it be: 
    async Task<Foo> ISomething.DoSomethingAsync() { 
     return await Task.Run(() => DoSomethingElse()); 
    } 
    // Or: 
    async Task<Foo> ISomething.DoSomethingAsync() { 
     return DoSomethingElse(); 
    } 
} 

Ich versuche blog mit Stephen Cleary zu halten, und ich weiß, keiner von ihnen tatsächlich keine Asynchron-Vorteile bietet, und ich bin ok damit. Die zweite scheint mir korrekter zu sein, da sie nicht vorgibt, etwas zu sein, was sie nicht ist, aber sie gibt eine Compiler-Warnung, und diese summieren sich und werden ablenkend.

Das wäre alles innerhalb von ASP.NET (sowohl Web MVC und WebAPI), wenn das einen Unterschied macht.

+0

Ihre „normale Async Implementierung“ ist nicht gültig ... Sie haben nichts zurückkehrt, noch Ihr Rückgabetyp ist 'Aufgabe '. –

+0

@ B.K. Danke, ich habe die Rückkehr vergessen. Fest. –

+0

Sie können 'Task.RunSynchronously' anstelle von' Task.Run' verwenden, um das 'async'-artige Ausnahmebildungsverhalten ohne redundantes' ThreadPool'-Offloading zu erhalten, wie [hier] beschrieben (http://stackoverflow.com/a/21082631/1768303). – Noseratio

Antwort

8

können Sie verzichten die async Modifikator zusammen und verwenden Task.FromResult eine erledigte Aufgabe synchron zurückzukehren:

Task<Foo> ISomething.DoSomethingAsync() 
{ 
    return Task.FromResult(DoSomethingElse()); 
} 

Diese kümmert sich um die Warnung und eine bessere Leistung hat, da es nicht die Zustandsmaschine Overhead eines braucht async Methode.

Allerdings tut dies change the semantics of exception handling ein bisschen. Wenn das ein Problem ist, dann sollten Sie die synchrone async Methode Ansatz verwenden und die Warnung akzeptieren (oder schalten Sie ihn mit einem Kommentar aus):

#pragma warning disable 1998 
    async Task<Foo> ISomething.DoSomethingAsync() 
#pragma warning restore 1998 
    { 
     return DoSomethingElse(); 
    } 

Als Stephen Cleary vorgeschlagen, dass Sie auch um diese Warnung nehmen (während die Beibehaltung Verfahren synchron) durch eine bereits abgeschlossene Aufgabe wartet:

async Task<Foo> ISomething.DoSomethingAsync() 
{ 
    await Task.FromResult(false); // or Task.CompletedTask in .Net 4.6 
    return DoSomethingElse(); 
} 
+1

Oder ein asynchroner Noop: 'erwarten Task.CompletedTask;' in .NET 4.6. –

+0

@StephenCleary Um die Warnung loszuwerden? Ist das nicht hacky? – i3arnon

+0

Cool, danke. Ich werde es mit 'await' aufrufen, also habe ich nicht wirklich darüber nachgedacht, die Methode nicht" asynchron "zu setzen. Ich werde beide Ansätze in Betracht ziehen - klingt wie einer für mich arbeiten würde. –

2

Es hängt wirklich davon ab, was Ihre Methode tut:

  • keine I/O, vernachlässigbare Menge an cpu arbeiten
  • CPU intensive Arbeit
  • I/O-intensive Arbeit

keine I/O, vernachlässigbare Menge an CPU-Arbeit

Sie das Ergebnis berechnen sollten synchron und erstellen das Ergebnis eine Aufgabe Halt .

Beachten Sie jedoch, dass beim Aufruf der Methode eine Ausnahme ausgelöst wird und nicht, wenn die Aufgabe erwartet wird. Für den letzteren Fall, die zu den anderen Arten von Arbeit kompatibel ist, sollten Sie async trotzdem verwenden:

async Task<Foo> ISomething.DoSomethingAsync() { 
    Foo result; 
    // do something not hurting performance 
    // no I/O here 
    return result; 
} 

CPU intensive Arbeit

Sie sollten eine Aufgabe mit Task.Run und tun, um die CPU-intensive Arbeit beginnen in der Aufgabe.

Task<Foo> ISomething.DoSomethingAsync() { 
    return Task.Run(() => { 
     Foo result; 
     // do some CPU intensive work here 
     return result; 
    }); 
} 

I/O-intensive Arbeit

sollten Sie verwenden die async Schlüsselwort und warten auf eine beliebige async I/O-Methode. Verwenden Sie keine synchronen E/A-Methoden.

async Task<Foo> ISomething.DoSomethingAsync() { 
    Stream s = new .... 

    // or any other async I/O operation 
    var data = await s.ReadToEndAsync(); 

    return new Foo(data); 
} 
+3

'Task.Run' fügt wirklich keinen Wert hinzu. Die Entscheidung, auf einen 'ThreadPool'-Thread zu wechseln, sollte wirklich dem Verbraucher überlassen werden. – i3arnon

+1

Da dies ein ASP.NET-Szenario ist, entfernt 'Task.Run' * * den Wert. –

Verwandte Themen