2012-05-13 1 views
5

Ich möchte die letzten C# 5.0-Async-Methoden verwenden, jedoch auf eine bestimmte Weise.Ist es möglich, die gleiche Async-Methodensignatur in C# 5.0 zu verwenden und später entscheiden zu können, wie diese Methode ausgeführt wird?

Betrachten Beispielcode folgende:

public abstract class SomeBaseProvider : ISomeProvider 
{ 
    public abstract Task<string> Process(SomeParameters parameters);  
} 

public class SomeConcreteProvider1 : SomeBaseProvider 
{ 

    // in this method I want to minimize any overhead to create/run Task 
    // NOTE: I remove here async from method signature, because basically I want to run 
    // method to run in sync! 
    public override Task<string> Process(SomeParameters parameters) { 

     string result = "string which I don't need any async code to get"; 

     return Task.Run(() => result); 
    } 
} 

public class SomeConcreteProvider2 : SomeBaseProvider 
{ 

    // in this method, it's OK for me to use async/await 
    public async override Task<string> Process(SomeParameters parameters) { 

     var data = await new WebClient().DownloadDataTaskAsync(urlToRequest); 

     string result = // ... here we convert data byte[] to string some way 

     return result; 

    } 
} 

Nun, wie ich Asynchron-Methoden verwenden werde (können Sie ignorieren, dass Verbraucher ASP.NET tatsächlich MVC4 app in meinem Fall ... es kann alles sein):

public class SomeAsyncController : AsyncController 
{ 

    public async Task<ActionResult> SomethingAsync(string providerId) 
    { 
     // we just get here one of providers I define above 
     var provider = SomeService.GetProvider(providerId); 

     // we try to execute here Process method in async. 
     // However we might want to actually do it in sync instead, if 
     // provider is actually SomeConcreteProvider1 object. 
     string result = await provider.Process(new SomeParameters(...)); 

     return Content(result); 
    } 
} 

Wie Sie sehen können I 2-Implementierungen haben, wird jeder durchführen anders: ein I in Asynchron ausgeführt werden soll und nicht blockieren Gewinde (SomeConcreteProvider2), während ein anderer ich synchron laufen zu können, wollen und erstellen Sie kein Task-Objekt usw. (das ich oben im Code nicht programmieren kann, d. h Ich erstelle hier eine neue Aufgabe!).

Es gibt bereits Fragen wie How would I run an async Task<T> method synchronously?. Ich möchte jedoch nichts synchron ausführen ... Ich möchte jeden Overhead vermeiden, wenn ich zur Codezeit (dh vor der Laufzeit) weiß, dass einige Methodenimplementierungen NICHT tatsächlich asynchron sind und keine Threads/I verwenden müssen/O Completion-Ports usw. Wenn Sie Code oben überprüfen, ist es einfach zu sehen, dass die Methode in SomeConcreteProvider1 im Grunde eine Zeichenfolge (html) erstellt, kann es sehr schnell bei der gleichen Ausführung Thread. Die gleiche Methode in SomeConcreteProvider2 muss Web Request erstellen, Web Response bekommen und sie irgendwie verarbeiten und ich möchte mindestens Web Request in Async machen, um die Blockierung des ganzen Threads während der Anfragezeit zu vermeiden (kann tatsächlich lange Zeit beendet werden).

Also die Frage ist: Wie organisiere ich meinen Code (verschiedene Methodensignaturen oder verschiedene Implementierungen, oder?) Um entscheiden zu können, wie man Methoden ausführt und JEDEN möglichen Overhead, der zum Beispiel durch Task.Run (.. .) in SomeConcreteProvider1.Process-Methode?

Update 1: offensichtliche Lösungen (ich glaube, einige von ihnen in dem Frageprozess), wie zum Beispiel einer statische Eigenschaft zu jedem Anbieter hinzufügen (sagen ‚isAsyncImplementation‘) und dann diese Eigenschaft überprüfen, um zu entscheiden, wie zu laufen Methode (mit erwarten oder ohne erwarten in Controller Aktion) haben auch einige Overhead: DI wollen etwas besser, wenn möglich: D

+0

P.S. Es war wirklich schwierig, einen richtigen Titel für diese Frage zu erstellen. Ich werde froh sein, wenn jemand es beheben kann :) Danke – Evereq

+0

Ich glaube, dass das Erstellen einer asynchronen Methode ohne irgendwelche Wartezeiten aufgrund von Task-Caching schneller sein kann. – SLaks

+0

@SLaks meinen Sie, dass mein Code in SomeConcreteProvider1.Process bereits schnell genug aufgrund einiger Task-Caching? – Evereq

Antwort

10

In Ihrem ersten Fall würde ich Rückkehr empfehlen:

Task.FromResult(result) 

, die eine fertige abgeschlossene Aufgabe zurückgibt.

http://msdn.microsoft.com/en-us/library/hh194922%28v=vs.110%29.aspx

+0

ah, weiß nicht darüber - scheint sehr gut Ansatz: D Danke :) – Evereq

+0

hm, scheint es, wenn ich Task.Run (() => Ergebnis) mit Task.FromResult (Ergebnis) Code kompiliert jedoch ersetzt Wenn ich die Controller-Aktion ausführe, gebe ich leere Inhalte zurück :) Ie könnten einige Probleme im Zusammenhang mit warten auf Task.FromResult !? Siehe zum Beispiel http://social.msdn.microsoft.com/Forums/nl-NL/async/thread/d9146792-1b9a-4807-a42e-29107c281cc4 usw. – Evereq

+3

@EvereQ - MVC4 Beta hat einen Fehler, wo es nicht tut Behandeln einer bereits abgeschlossenen Aufgabe, die zurückgegeben wird. Die empfohlene Beta-Problemumgehung ist das Hinzufügen eines Task.Yields am Anfang einer solchen Async-Methode, aber Task.Run klingt in diesem Fall wie eine brauchbare Umgehungsmöglichkeit für Sie. –

0

Ihr Design sieht gut aus.

Für die SomeConcreteProvider1 Fall können Sie einen TaskCompletionSource

verwenden Gibt eine Task ohne Parallelität zu erfordern.

Auch in der Verbraucher-Code wird await nicht ergeben, wenn die Task bereits abgeschlossen ist. Dies wird als "schneller Pfad" bezeichnet und macht die Methode effektiv synchron.

+0

hm, nicht sicher, wie TaskCompletionSource hier "einfach" zu verwenden ist ... I.e. Sie wollen TaskCompeltionSource zurückgeben und Client entscheiden lassen, wie Code ausgeführt wird? Allerdings ist die Frage, wie Client wissen, ist Async-oder Sync-Ausführung für bestimmte Anbieter erforderlich, wenn Sie nur TaskCompletionSource Objekt zurückgeben? ;-) – Evereq

+1

Sie können ein 'TaskCompletionSource ' -Objekt in 'SomeConcreteProvider1.Process' erstellen, das result/cancellation/exception setzen und die Eigenschaft' Task' zurückgeben. Wenn Sie nur das Ergebnis setzen werden, können Sie 'Task.FromResult' verwenden, wie gesagt. Wie auch immer, durch Zurückgeben einer "Aufgabe", die bereits abgeschlossen ist, wird das "Warten" im Verbrauchercode nicht nachgeben - die Ausführung wird einfach synchron fortgesetzt. –

+0

ah, es scheint so, als hätte ich Probleme aus irgendeinem Grund auf Task.FromResult zu warten (siehe meinen Kommentar, um mit @spender zu antworten). Will versuchen aber Ansatz mit TaskCompletionSource – Evereq

Verwandte Themen