2012-08-14 10 views
11

Tengo dos métodos asincrónicos Task<int> DoInt() y Task<string> DoString(int value). Tengo un tercer método asincrónico Task<string> DoBoth(int value) cuyo objetivo es ejecutar de forma asincrónica DoInt(), la salida de la tubería es DoString(int) y el resultado es el resultado de DoBoth().Continuación de una tarea <T> en una tarea <U> sin bloqueo en el resultado

Las limitaciones importantes son:

  1. Puede que no tenga el código fuente a DoInt() o DoString(), así que no puedo modificarlos.
  2. no quiero bloquear en cualquier punto
  3. salida (resultado) de una tarea debe ser pasado como entrada a la siguiente
  4. La salida final (resultado) es lo que yo quiero ser el resultado de DoBoth()

La clave es que ya tengo métodos que son asíncronos (es decir, devolver tarea) y quiero canalizar datos de tarea a tarea, sin bloquear en el camino, devolviendo el resultado final en la tarea inicial. Puedo hacer todo lo siguiente, excepto no bloquear en el siguiente código:

Código ejemplo:

// Two asynchronous methods from another library, i.e. can't be changed 
Task<int> DoInt(); 
Task<string> DoString(int value); 

Task<string> DoBoth() 
{ 
    return DoInt().ContinueWith<string>(intTask => 
    { 
     // Don't want to block here on DoString().Result 
     return DoString(intTask.Result).Result; 
    }); 
} 

Desde Task<string> DoString(int) ya es un método asíncrono, ¿cómo puedo crear una continuación limpiamente sin bloqueo a ella? De hecho, quiero crear una continuación de una Tarea existente y no de una Func.

+1

¿'DoInt' toma un int como argumento, o no? Lo tiene en ambos sentidos en su publicación ... –

+0

@ReedCopsey - He arreglado la firma para DoInt(). Si tiene un parámetro o no, no es importante en el ejemplo, pero la inconsistencia en mi ejemplo es confusa :) También renombré valueTask => intTask para mayor claridad. – MikeJansen

Respuesta

8

Usted puede escribir toda la cadena usando TaskExtensions.Unwrap como:

Task<string> DoBoth(int value) 
{ 
    Task<Task<string>> task = DoInt(value).ContinueWith(valueTask => 
    { 
     return DoString(valueTask.Result); 
    }); 

    return task.Unwrap(); 
} 

Tenga en cuenta que esto supone que DoInt se define como Task<int> DoInt(int value);, que difiere de su descripción, sino que sigue el ejemplo de código.

Si DoInt no toma un argumento int (coincidentes con sus declaraciones), esto podría convertirse en:

Task<string> DoBoth() 
{ 
    return DoInt().ContinueWith(t => DoString(t.Result)).Unwrap(); 
} 

Como extra - usando C# 5 y .NET 4.5 (o el paquete asíncrono de orientación) , puede escribir esto como:

async Task<string> DoBoth(int value) 
{ 
    int first = await DoInt(value); 
    return await DoString(first); 
} 
+0

'valueTask.Result' no se bloqueará, pero' DoString (valueTask.Result) .Result' se bloqueará hasta que se complete la tarea devuelta por 'DoString()'. Y al bloquear, no me refiero a que la persona que llamó originalmente estaba bloqueada; Solo me refiero a un hilo que se encuentra allí potencialmente inactivo esperando a que se complete otra tarea (en este caso, el hilo ejecuta un delegado asíncrono, pero sigue siendo un hilo desde la perspectiva de los recursos del sistema). – MikeJansen

+0

¡Genial! Reed, si pudieras aclarar; ¿No habrá bloqueo aunque tome 'valueTask.Result'? –

+1

@AndersGustafsson No - en el punto en el que tomas el resultado, estás en una continuación. 'valueTask' se garantiza que se completará en ese punto, por lo que' .Result' se vuelve no bloqueante. (Sin embargo, aún puede generar una excepción si la tarea se canceló o falló, pero nunca se bloqueará.) –

Cuestiones relacionadas