2012-08-14 15 views
6

Próbuję użyć Task.WhenAll, aby poczekać na zakończenie wielu zadań.Jak poprawnie używać Task.WhenAll()

Mój kod jest poniżej - ma uruchomić wiele zadań asynchronicznych, z których każda pobiera trasę magistrali, a następnie dodaje je do lokalnej tablicy. Jednak Task.WhenAll (...) zwraca natychmiast, a liczba lokalnych tablic tras wynosi zero. Wydaje się to dziwne, ponieważ oczekiwałbym, że różne "oczekiwania" w każdym zadaniu oznaczają, że przepływ jest zawieszony, a zadanie nie powraca, dopóki nie zostanie zakończone.

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

Oczywiście robię coś nie tak - ale co?

+0

Czy chciał sprawdzić, czy ostateczna 'Task' nie jest w stanie nic zarzucić? –

+0

Myślę, że może być coś złego 'oczekuj wewnątrz pętli foreach 'wewnątrz dyspozytora. Wątek interfejsu użytkownika będzie obserwowany i wyświetlany natychmiast? –

+0

@ie. Tak, jego stan to RanToCompletion. Stan wszystkich zadań w tablicy to również RanToCompletion, chociaż kiedy faktycznie je sprawdzam pojedynczo, pole wyniku każdego z nich to WaitingForActivation –

Odpowiedz

6

ta spadła do podstawowego braku zrozumienia w jaki sposób asynchroniczny-czekają naprawdę działa.

Wewnętrznym zadaniem było przywrócenie przepływu do zewnętrznego zadania, które następnie zakończyło się przed oczekiwaniem na powrót.

Aby osiągnąć to, co chciałem, potrzebna byłaby następująco:

 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 
     } 

Dzięki Dave Smits

5

Podejrzewam, że problem polega na połączeniu z numerem Task.Factory.StartNew(). Podejrzewam, że kończysz z Task<Task>, a dowiesz się tylko, kiedy to jest skuteczne.

Spróbuj to zamiast:

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

Dzięki. Zakładam, że reszta mojej metody będzie kontynuowana jak poprzednio, tj. Doda zadanie do tablicy zadań, a następnie wywoła funkcję Task.WhenAll (...) w tablicy. W takim razie, w jaki sposób podejście to porównuje się do tego w mojej odpowiedzi? Czy są jakieś zalety? –

+0

@CarlosP: Szczerze mówiąc, nie mam wystarczająco dużo kontekstu, aby komentować, co jest lepszym podejściem. Ale tak, reszta metody będzie kontynuowana jak poprzednio. –