2012-05-13 2 views
5

Quiero utilizar métodos asincrónicos recientes de C# 5.0, pero de alguna manera específica.¿Es posible utilizar la misma firma de método asíncrono en C# 5.0 y poder decidir más adelante cómo ejecutar dicho método?

Considere ejemplo de código siguiente:

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; 

    } 
} 

Ahora cómo voy a utilizar métodos asincrónicos (se puede ignorar hecho de que los consumidores realmente ASP.NET MVC4 aplicación en mi caso ... puede ser cualquier cosa):

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); 
    } 
} 

Como se puede ver tengo 2 implementaciones, cada uno va a realizar de forma diferente: uno Quiero correr en asíncrono y no me cuadra hilo (SomeConcreteProvider2), mientras que otro Quiero ser capaz de ejecutar de forma sincronizada y no crea ningún objeto Task, etc. (que no puedo codificar en el código anterior, es decir, ¡Sí creo una nueva tarea aquí!).

Ya hay preguntas como How would I run an async Task<T> method synchronously?. Sin embargo, no quiero ejecutar algo en sincronización ... quiero evitar cualquier sobrecarga si sé en el momento del código (es decir, antes del tiempo de ejecución) que algunas implementaciones de métodos NO serán realmente asincrónicas y no necesitarán usar ningún hilo/I/O puertos de finalización, etc. Si comprueba el código anterior, es fácil ver que el método en SomeConcreteProvider1 básicamente construirá una cadena (html), puede hacerlo muy rápidamente en el mismo hilo de ejecución. Sin embargo, el mismo método en SomeConcreteProvider2 necesitará crear una solicitud web, obtener respuesta web y procesarla de algún modo, y quiero hacer al menos una solicitud web en Async para evitar el bloqueo de un hilo completo durante el tiempo de solicitud (puede dejarlo mucho tiempo en realidad).

Así que la pregunta es: ¿cómo organizar mi código (diferentes firmas de métodos o diferentes implementaciones, o?) Para poder decidir cómo ejecutar el método y evitar CUALQUIER sobrecarga posible que cause, por ejemplo, Task.Run (.. .) en SomeConcreteProvider1.Process method?

Actualización 1: soluciones obvias (creo que algunos de ellos durante el proceso de que se trate), como por ejemplo, para añadir una propiedad estática a cada uno de los proveedores (decir 'isAsyncImplementation') y luego verificar que la propiedad de decidir cómo para ejecutar el método (con aguardar o sin esperar en la acción del controlador) también hay algo de sobrecarga: DI si quiere algo mejor si es posible: D

+0

P.S. fue realmente difícil crear el título correcto para esta pregunta. Estaré contento si alguien puede arreglarlo :) Gracias – Evereq

+0

Creo que la creación de un método asíncrono sin espera puede ser más rápido debido al almacenamiento en caché de tareas. – SLaks

+0

@SLaks ¿quiere decir que mi código en SomeConcreteProvider1.Process ya es lo suficientemente rápido debido al almacenamiento en caché de algunas tareas? – Evereq

Respuesta

10

En el primer caso, recomendaría volver:

Task.FromResult(result) 

que devuelve una tarea listo completado.

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

+0

ah, no sé sobre eso - parece muy buen enfoque: D Gracias :) – Evereq

+0

hm, parece que si reemplazo Task.Run ((= = resultado) con compilaciones de código Task.FromResult (result), sin embargo cuando realmente ejecuto la acción del controlador devuelve contenido vacío :) Es decir podría haber algunos problemas relacionados con la espera de Task.FromResult !? Consulte, por ejemplo, http://social.msdn.microsoft.com/Forums/nl-NL/async/thread/d9146792-1b9a-4807-a42e-29107c281cc4 etc. – Evereq

+3

@EvereQ - MVC4 beta tiene un error que no funciona manejar una tarea ya completada que se devuelve. La solución beta recomendada es agregar Task.Yield en la parte superior de cualquiera de dichos métodos de sincronización, pero Task.Run suena como una solución viable para usted en este caso. –

0

Su diseño se ve bien.

Para el caso SomeConcreteProvider1, se puede utilizar un TaskCompletionSource

Esto devuelve una Task sin requerir la concurrencia.

Además, en el código del consumidor, await no cederá si el Task ya se ha completado. Esto se denomina "ruta rápida" y hace que el método sea sincrónico.

+0

hm, no estoy seguro de cómo "fácil" utilizar TaskCompletionSource aquí ... Es decir. ¿Quiere devolver TaskCompeltionSource y dejar que el cliente decida cómo ejecutar el código? Sin embargo, la pregunta es cómo sabrá el cliente que se requiere sincronización o ejecución de sincronización para un proveedor determinado si solo devuelve el objeto TaskCompletionSource . ;-) – Evereq

+1

Puede crear un objeto 'TaskCompletionSource ' en 'SomeConcreteProvider1.Process', establecer el resultado/cancelación/excepción y devolver la propiedad' Task'. Si solo va a establecer el resultado, puede usar 'Task.FromResult' como dijo spender. De cualquier forma, devolviendo una 'Tarea 'que ya está completa, el' await' en el código del consumidor no cederá - la ejecución continuará síncronamente. –

+0

ah, parece que tengo problemas para esperar Task.FromResult por alguna razón (ver mi comentario para responder por @spender). Sin embargo, intentará acercarse con TaskCompletionSource – Evereq

Cuestiones relacionadas