2012-08-14 4 views
6

Sto cercando di utilizzare Task.WhenAll per attendere il completamento di più attività.Come utilizzare correttamente Task.WhenAll()

Il mio codice è sotto - si suppone che avvii più task asincroni, ognuno dei quali recupera un percorso di bus e quindi li aggiunge a un array locale. Tuttavia, Task.WhenAll (...) restituisce immediatamente e il conteggio dell'array delle rotte locali è zero. Questo sembra strano, dal momento che mi aspetterei che le varie dichiarazioni di "attesa" all'interno di ogni Task significassero che il flusso è sospeso e l'attività non ritorna finché non è terminata.

 List<Task> monitoredTasks = new List<Task>(); 
     foreach (BusRouteIdentifier bri in stop.services) 
     { 
      BusRouteRequest req = new BusRouteRequest(bri.id); 

      // Start a new task to fetch the route for each stop 
      Task getRouteTask = Task.Factory.StartNew(async() => 
      { 
       var route = await BusDataProviderManager.DataProvider.DataBroker.getRoute(req); 

        // Add the route to our array (on UI thread as it's observed) 
        await dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, delegate 
        { 
         this.routes.Add(route); 
        }); 
      }); 

      // Store the task in our monitoring list 
      monitoredTasks .Add(getRouteTask); 
     } 

     Debug.WriteLine("Awaiting WHENALL"); 
     await Task.WhenAll(monitoredTasks); 
     Debug.WriteLine(string.Format("WHENALL returned (routes count is {0} ", this.routes.Count)); 

     this.OnWillEndFetchingRoutes(new EventArgs()); 

Ovviamente sto facendo qualcosa di sbagliato - ma cosa?

+0

hai provato a verificare se la finale 'Task' non è in stato di errore? –

+0

Penso che potrebbe essere qualcosa di sbagliato 'attendi il dispatcher' all'interno del ciclo foreach. Il thread dell'interfaccia utente verrà osservato e visualizzato immediatamente? –

+0

@ie. Sì, il suo stato è RanToCompletion. Anche lo stato di tutti i Compiti dell'array è RanToCompletion, sebbene quando li ispeziono singolarmente, il campo Risultato di ognuno di essi è WaitingForActivation –

risposta

6

questo era giù per una base mancanza di comprensione di come async-attendere funziona davvero.

L'attività interna stava restituendo il flusso all'attività esterna, che è poi terminata prima che l'attesa fosse restituita.

Per ottenere quello che volevo, avevo bisogno di refactoring come segue:

 List<Task<BusRoute>> routeRetrievalTasks = new List<Task<BusRoute>>(); 
     foreach (BusRouteIdentifier bri in stop.services) 
     { 
      BusRouteRequest req = new BusRouteRequest(bri.id); 
      routeRetrievalTasks.Add(BusDataProviderManager.DataProvider.DataBroker.getRoute(req)); 
     } 

     foreach (var task in routeRetrievalTasks) 
     { 
      var route = await task; 
      this.routes.Add(route); // triggers events 
     } 

Grazie a Dave Smits

5

Sospetto che il problema sia la vostra chiamata a Task.Factory.StartNew(). Ho il sospetto che tu stia finendo con un Task<Task>, e stai scoprendo solo quando è effettivamente avviato l'attività.

Prova a modificare:

Func<Task> taskFunc = async() => 
{ 
    var route = await BusDataProviderManager.DataProvider.DataBroker.getRoute(req); 

    // Add the route to our array (on UI thread as it's observed) 
    await dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, delegate 
    { 
     this.routes.Add(route); 
    }); 

} 

Task getRouteTask = Task.Run(taskFunc); 
+0

Grazie. Suppongo che il resto del mio metodo continui come prima, cioè aggiungo l'attività alla serie di attività, quindi chiama Task.WhenAll (...) sull'array. In tal caso, come si confronta questo approccio con quello della mia risposta? Ci sono dei vantaggi? –

+0

@CarlosP: Ad essere sinceri, non ho davvero un contesto sufficiente per commentare quale sia l'approccio migliore. Ma sì, il resto del tuo metodo continuerebbe come prima. –