2012-09-09 21 views
72

He estado tratando durante algún tiempo para conseguir algo que pensé que sería sencillo trabajar con .NET 4.5ejecutar dos tareas asíncronas en paralelo y recogen los resultados en .NET 4.5

Quiero disparar dos tareas en ejecución largos al mismo tiempo y recoger las
resultados en la mejor C# 4.5 manera (RTM)

las siguientes obras pero no me gusta porque:

  • Quiero Sleep ser un método asíncrono por lo puede await oth er métodos
  • Parece torpe con Task.Run()
  • ¡No creo que esto esté usando ninguna característica de lenguaje nuevo!

código de trabajo:

public static void Go() 
{ 
    Console.WriteLine("Starting"); 

    var task1 = Task.Run(() => Sleep(5000));  
    var task2 = Task.Run(() => Sleep(3000)); 

    int totalSlept = task1.Result + task2.Result; 

    Console.WriteLine("Slept for a total of " + totalSlept + " ms"); 
} 

private static int Sleep(int ms) 
{ 
    Console.WriteLine("Sleeping for " + ms); 
    Thread.Sleep(ms); 
    Console.WriteLine("Sleeping for " + ms + " FINISHED"); 
    return ms; 
} 

código no laborables:

Actualización: Esta realmente funciona y es la forma correcta de hacerlo, el único problema es la Thread.Sleep

Este código no funciona porque la cal l a Sleep(5000) inicia inmediatamente la tarea ejecutándose para que Sleep(1000) no se ejecute hasta que se complete. Esto es cierto aunque Sleep es async y no estoy usando await o llamando al .Result demasiado pronto.

pensé que tal vez hay una manera de conseguir una que no se corre Task<T> llamando a un método async así que entonces podría llamar Start() en las dos tareas, pero no puedo encontrar la manera de conseguir un Task<T> de llamar a un asíncrono método.

public static void Go() 
{ 
    Console.WriteLine("Starting"); 

    var task1 = Sleep(5000); // blocks 
    var task2 = Sleep(1000); 

    int totalSlept = task1.Result + task2.Result; 

    Console.WriteLine("Slept for " + totalSlept + " ms"); 
} 

private static async Task<int> Sleep(int ms) 
{ 
    Console.WriteLine("Sleeping for " + ms); 
    Thread.Sleep(ms); 
    return ms; 
} 
+0

nota: Ir haciendo un método asíncrono hace ninguna diferencia –

+3

bloque que está pasando en ' task1.Result' no en 'var task1 = Sleep (5000)' porque su método Sleep sin una palabra clave await es sincrónico – Arvis

Respuesta

34

Debe usar Task.Delay en lugar de Sleep para la programación asincrónica y luego usar Task.WhenAll para combinar los resultados de la tarea. Las tareas se ejecutarían en paralelo.

public class Program 
    { 
     static void Main(string[] args) 
     { 
      Go(); 
     } 
     public static void Go() 
     { 
      GoAsync(); 
      Console.ReadLine(); 
     } 
     public static async void GoAsync() 
     { 

      Console.WriteLine("Starting"); 

      var task1 = Sleep(5000); 
      var task2 = Sleep(3000); 

      int[] result = await Task.WhenAll(task1, task2); 

      Console.WriteLine("Slept for a total of " + result.Sum() + " ms"); 

     } 

     private async static Task<int> Sleep(int ms) 
     { 
      Console.WriteLine("Sleeping for {0} at {1}", ms, Environment.TickCount); 
      await Task.Delay(ms); 
      Console.WriteLine("Sleeping for {0} finished at {1}", ms, Environment.TickCount); 
      return ms; 
     } 
    } 
+1

Esta es una gran respuesta ... pero pensé que esta respuesta era incorrecta hasta que la ejecuté. entonces entendí. Realmente se ejecuta en 5 segundos. El truco es NO esperar las tareas de inmediato, en su lugar, aguarde en Task.WhenAll. –

3

Mientras que su método Sleep es asíncrono, Thread.Sleep no lo es. La idea de asincronizar es reutilizar un único hilo, no iniciar varios hilos. Debido a que ha bloqueado el uso de una llamada sincrónica a Thread.Sleep, no va a funcionar.

Supongo que Thread.Sleep es una simplificación de lo que realmente quiere hacer. ¿Puede su implementación real codificarse como métodos asíncronos?

Si necesita ejecutar varias llamadas de bloqueo sincrónicas, busque en otro lugar, creo!

+0

gracias Richard - sí, parece que funciona como se esperaba cuando en realidad uso mi servicio llame al –

+0

y luego, ¿cómo ejecutar la función de sincronización? Tengo una aplicación que hace mucho cambio de archivo y espera por archivo, alrededor de 5 segundos, y luego otro proceso, cuando "cuando para todo" primero se ejecuta primero, luego segundo, aunque dije: 'var x = y() ', y no' var x = await y() 'o' y(). wait() 'pero todavía espera todo el camino, y si async no lo maneja solo, ¿qué debo hacer? TEN EN CUENTA que y está decorado con asincrónico, y espero que haga todo dentro de "when all", no exactamente donde está asignado, EDIT: justo cuando mi compañero dijo, probemos 'Task.Factory', y él lo dijo funcionó cuando salí al lado de esta clase – deadManN

1

Este artículo ayudó a explicar muchas cosas. Está en estilo de preguntas frecuentes.

Async/Await FAQ

Esta parte explica por qué Thread.Sleep se ejecuta en el mismo hilo original - que conduce a la confusión inicial.

¿Tiene la palabra clave “asíncrono” causa la invocación de un método de hacer cola para ThreadPool? Para crear un nuevo hilo? Para lanzar un cohete a Mars?

No. No. Y no. Ver las preguntas anteriores. La palabra clave "async" indica al compilador que "aguarde" puede usarse dentro del método , de modo que el método puede suspenderse en un punto de espera y tener su ejecución reanudada de forma asíncrona cuando finaliza la instancia esperada . Esta es la razón por la cual el compilador emite una advertencia si no hay "en espera" dentro de un método marcado como "asíncrono".

80
async Task LongTask1() { ... } 
async Task LongTask2() { ... } 
... 
{ 
    Task t1 = LongTask1(); 
    Task t2 = LongTask2(); 
    await Task.WhenAll(t1,t2); 
    //now we have t1.Result and t2.Result 
} 
+1

I +1 porque declara t1, t2 como Tarea, que es la manera correcta. – Minime

+9

Creo que esta solución requiere que el método Go también sea asincrónico, lo que significa que expone la capacidad de ser asincrónico. Si desea algo más parecido al caso de Askers donde el método 'Go' de la persona que llama es sincrónico, pero quiere completar dos tareas independientes de forma asíncrona (es decir, ninguna necesita completarse antes que la otra, pero ambas deben completarse antes de las ejecuciones)' 'Tarea .WaitAll' ** sería mejor, y no necesita la palabra clave await, por lo tanto, no es necesario que el método de llamada "Go" sea asincrónico. ** Ninguno de los dos enfoques es mejor, solo se trata de cuál es tu objetivo. ** – AaronLS

+1

Metodo de Void: 'async void LongTask1() {...}' no tiene propiedad Task.Result. Use Tarea sin T en tal caso: 'async Task LongTask1()'. – Arvis

0

Para responder a esta cuestión:

Quiero Dormir ser un método asíncrono por lo que se espera de otros métodos

tal vez puede reescribir la función Sleep como esto:

private static async Task<int> Sleep(int ms) 
{ 
    Console.WriteLine("Sleeping for " + ms); 
    var task = Task.Run(() => Thread.Sleep(ms)); 
    await task; 
    Console.WriteLine("Sleeping for " + ms + "END"); 
    return ms; 
} 

static void Main(string[] args) 
{ 
    Console.WriteLine("Starting"); 

    var task1 = Sleep(2000); 
    var task2 = Sleep(1000); 

    int totalSlept = task1.Result +task2.Result; 

    Console.WriteLine("Slept for " + totalSlept + " ms"); 
    Console.ReadKey(); 
} 

de ejecutar este código de salida de voluntad:

Starting 
Sleeping for 2000 
Sleeping for 1000 
*(one second later)* 
Sleeping for 1000END 
*(one second later)* 
Sleeping for 2000END 
Slept for 3000 ms 
1

Es fin de semana ahora!

public static void Go() { 
    Console.WriteLine("Start fosterage...\n"); 
    var t1 = Sleep(5000, "Kevin"); 
    var t2 = Sleep(3000, "Jerry"); 
    var result = Task.WhenAll(t1, t2).Result; 

    Console.WriteLine("\nMy precious spare time last for only {0}ms", result.Max()); 
    Console.WriteLine("Press any key and take same beer..."); 
    Console.ReadKey(); 
} 

private static async Task<int> Sleep(int ms, string n) { 
     Console.WriteLine("{0} going to sleep for {1}ms :)", n, ms); 
     await Task.Delay(ms); 
     Console.WriteLine("{0} waked up after {1}ms :(", n, ms); 
     return ms; 
} 

Ordinaria Task.Factory ejemplo:

private static Task<int> Sleep(int ms, string n) { 
     return Task.Factory.StartNew(() => { 
      Console.WriteLine("{0} going to sleep for {1}ms :)", n, ms); 
      Thread.Sleep(ms); 
      Console.WriteLine("{0} waked up after {1}ms :(", n, ms); 
      return ms; 
     }); 
    } 

misterioso TaskCompletionSource ejemplo:

private static Task<int> Sleep(int ms, string n) { 
    var tcs = new TaskCompletionSource<int>(); 
    Console.WriteLine("{0} going to sleep for {1}ms :)", n, ms); 
    var t = Task.Factory.StartNew(() => { 
     Thread.Sleep(ms); 
     Console.WriteLine("{0} waked up after {1}ms :(", n, ms); 
     tcs.SetResult(ms); 
    }); 
    return tcs.Task; 
} 
Cuestiones relacionadas