2012-09-09 13 views
72

Ho cercato per un po 'per ottenere qualcosa che ho pensato che sarebbe stato semplice lavorare con .NET 4.5eseguire due compiti asincroni in parallelo e raccogliere i risultati in .NET 4.5

voglio sparare due attività in esecuzione lunghi allo stesso tempo e raccogliere i
risultati in nel miglior C# 4.5 (RTM) modo

i seguenti lavori ma non mi piace perché:

  • voglio Sleep essere un metodo asincrono così può await oth er metodi
  • Sembra maldestro con Task.Run()
  • Non penso che questo stia usando addirittura nessuna nuova funzionalità linguistica!

codice di lavoro:

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

codice non lavoro:

Update: Questo funziona davvero ed è il modo corretto per farlo, l'unico problema è il Thread.Sleep

Questo codice non funziona perché il l a Sleep(5000) avvia immediatamente l'attività in esecuzione, pertanto Sleep(1000) non viene eseguito fino al completamento. Questo è vero anche se Sleep è async e non sto utilizzando await o chiamando .Result troppo presto.

ho pensato che forse c'è un modo per ottenere un non-esecuzione Task<T> chiamando un metodo async così ho potuto quindi chiamare Start() sui due compiti, ma io non riesco a capire come ottenere un Task<T> da chiamare un asincrona metodo.

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: fare Andare un metodo asincrono fa differenza –

+3

blocco che sta accadendo a ' task1.Result' non in 'var task1 = Sleep (5000)' perché il metodo di sospensione senza una parola chiave await è sincrono. – Arvis

risposta

34

È necessario utilizzare Task.Delay anziché Sleep per la programmazione asincrona e quindi utilizzare Task.WhenAll per combinare i risultati dell'attività. I compiti sarebbero eseguiti in parallelo.

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

Questa è un'ottima risposta ... ma ho pensato che fosse sbagliato rispondere finché non l'ho eseguito. allora ho capito. Funziona davvero in 5 secondi. Il trucco è NON attendere le attività immediatamente, ma attendere su Task.WhenAll. –

3

Mentre il metodo di Sleep è asincrono, Thread.Sleep non è. L'intera idea di async è di riutilizzare un singolo thread, non di avviare più thread. Poiché hai bloccato l'utilizzo di una chiamata sincrona a Thread.Sleep, non funzionerà.

Suppongo che Thread.Sleep sia una semplificazione di ciò che si desidera effettivamente fare. La tua implementazione effettiva può essere codificata come metodo asincrono?

Se è necessario eseguire più chiamate di blocco sincrone, guarda altrove, penso!

+0

grazie Richard - sì, sembra funzionare come previsto quando effettivamente uso la mia chiamata di servizio –

+0

allora come eseguire async? Ho un'applicazione che esegue molti cambi di file e aspetta file, circa 5 secondi, e poi un altro processo, quando "quando per tutti" prima esegue prima, poi secondo, anche se ho detto: 'var x = y() ', e non' var x = await y() 'o' y(). wait() 'ma aspetta ancora fino in fondo, e se async non lo gestisce da solo, cosa dovrei fare? NOTA che y è decorato con async, e mi aspetto che faccia tutto dentro quando tutto, non proprio dove è stato assegnato, EDIT: ho appena detto al mio partner, proviamo 'Task.Factory', e lo ha detto ha funzionato quando ho rotto il lato di questa classe – deadManN

1

Questo articolo ha contribuito a spiegare molte cose. È in stile FAQ.

Async/Await FAQ

Questa parte spiega perché Thread.Sleep viene eseguito sullo stesso thread originale - che porta alla mia confusione iniziale.

Condivide la parola chiave “asincrono” causa l'invocazione di un metodo di fare la coda per ThreadPool? Per creare un nuovo thread? Per lanciare una nave razzo su Mars?

No. No. E no. Vedi le domande precedenti. La parola chiave "async" indica al compilatore che "attende" può essere utilizzato all'interno del metodo , in modo tale che il metodo possa sospendere in un punto di attesa e avere l'esecuzione riprenda in modo asincrono quando l'istanza attesa completa. Questo è il motivo per cui il compilatore emette un avviso se non ci sono "attende" all'interno di un metodo contrassegnato come "async".

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 perché dichiari t1, t2 come Attività, che è la strada giusta. – Minime

+9

Credo che questa soluzione richieda anche il metodo Go asincrono, il che significa che espone la possibilità di essere asincroni. Se si desidera qualcosa di più come il caso degli utenti in cui il metodo 'Go' del chiamante è sincrono, ma si desidera completare due attività indipendenti in modo asincrono (ovvero non è necessario completare prima dell'altro, ma entrambe devono completarsi prima che le esecuzioni continuino) quindi **' Compito .WaitAll' ** sarebbe migliore, e non hai bisogno della parola chiave await, quindi non è necessario che il metodo 'Go' di chiamata sia asincrono stesso. ** Né l'approccio è migliore, è solo una questione di quale sia il tuo obiettivo. ** – AaronLS

+1

Metodo void: 'vuoto asincrono LongTask1() {...}' non ha proprietà Task.Result. Utilizzare Task senza T in questo caso: 'Task async LongTask1()'. – Arvis

0

Per rispondere a questo punto:

voglio dormire di essere un metodo asincrono in modo che possa attendere altri metodi

si può forse riscrivere la funzione Sleep in questo modo:

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

in esecuzione questa uscita codice:

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

E 'fine settimana ora!

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 esempio:

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

Mysterious TaskCompletionSource esempio:

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; 
} 
Problemi correlati