2012-07-25 14 views
15
var tasks = new List<Task>(); 

foreach (var guid in guids) 
{ 
    var task = new Task(...); 
    tasks.Add(task); 
} 

foreach (var task in tasks) 
{ 
    task.Start(); 
    Task.WaitAll(task); 
} 

To jest uruchomienie wątku interfejsu użytkownika. Muszę wykonywać wszystkie zadania w zmiennych zadań jeden po drugim. Problem polega na tym, że wywołuję Task.WaitAll (zadanie), blokowanie interfejsu użytkownika. Jak mogę wykonać poniższą logikę bez zawieszania się interfejsu?Łańcuch zadań (czekaj na ukończenie poprzedniego zadania)

+4

Więc dlaczego nie utworzysz jednego dużego delegata? – SLaks

+0

, aby dodać do tego, co powiedział @SLaks, jeśli masz tylko listę rzeczy do uruchomienia, użycie czegoś takiego jak BackgroundWorker może być prostsze. Prawdziwym nieznanym jest to, co chcesz zrobić, gdy praca w tle zostanie ukończona - zakładając, że jest to jakaś aktualizacja UI, BackgroundWorker obsługuje to już dla ciebie, uruchamiając ukończonego delegata w wątku UI. –

Odpowiedz

22

To nie jest zadanie Chaining.

Należy wykonać łańcuchowanie zadań za pomocą ContinueWith. Ostatnie zadanie wymagałoby aktualizacji interfejsu użytkownika.

Task.Factory.StartNew(() => DoThis()) 
    .ContinueWith((t1) => DoThat()) 
    .ContinueWith((t2) => UpdateUi(), 
     TaskScheduler.FromCurrentSynchronizationContext()); 

Uwaga ostatnia linia ma TaskScheduler.FromCurrentSynchronizationContext() to zadanie zapewni będą uruchamiane w kontekście synchronizacji (UI wątku).

4

Trzeba użyć continutations:

lastTask.ContinueWith(() => newTask.Start()); 
14

Najlepszym sposobem jest użycie Task Parallel Library (TPL) i kontynuacje. Kontynuacja nie tylko pozwala utworzyć przepływ zadań, ale także obsługuje wyjątki. Jest to great introduction dla licencji TPL. Ale aby dać pewne wyobrażenie ...

można rozpocząć zadania OC korzystając

Task task = Task.Factory.StartNew(() => 
{ 
    // Do some work here... 
}); 

teraz, aby rozpocząć drugie zadanie podczas będącego krokiem wykończenia zadania (w wyniku błędu lub pomyślnie) można użyć metody ContinueWith

Task task1 = Task.Factory.StartNew(() => Console.WriteLine("Antecedant Task")); 
Task task2 = task1.ContinueWith(antTask => Console.WriteLine("Continuation...")); 

tak szybko jak task1 finalizuje, nie powiedzie się lub zostanie anulowane task2 „pożarów-up” i zaczyna biec. Zauważ, że jeśli task1 zakończyłoby się przed osiągnięciem drugiej linii kodu task2, byłoby zaplanowane do natychmiastowego wykonania. Argument antTask przekazany do drugiej wartości lambda jest odniesieniem do zadania poprzedzającego. Zobacz this link dla bardziej szczegółowych przykładów ...

Można także przekazać kontynuacje wyniki od poprzednika zadania

Task.Factory.StartNew<int>(() => 1) 
    .ContinueWith(antTask => antTask.Result * 4) 
    .ContinueWith(antTask => antTask.Result * 4) 
    .ContinueWith(antTask =>Console.WriteLine(antTask.Result * 4)); // Prints 64. 

Note. Przeczytaj informacje na temat obsługi wyjątków w pierwszym odsyłaczu, ponieważ może to spowodować, że nowicjusz w błąd na TPL.

Ostatnią rzeczą, na którą należy zwrócić szczególną uwagę, są zadania dla dzieci. Zadania podrzędne to te, które są tworzone jako AttachedToParent. W takim przypadku kontynuacja nie będzie działać do momentu ukończenia wszystkich zadań podrzędnych:

TaskCreationOptions atp = TaskCreationOptions.AttachedToParent; 
Task.Factory.StartNew(() => 
{ 
    Task.Factory.StartNew(() => { SomeMethod() }, atp); 
    Task.Factory.StartNew(() => { SomeOtherMethod() }, atp); 
}).ContinueWith(cont => { Console.WriteLine("Finished!") }); 

Mam nadzieję, że to pomoże.

Powiązane problemy