Целевая цепочка (дождитесь завершения предыдущей задачи)

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

Выполняется поток пользовательского интерфейса. Мне нужно выполнить все задачи в переменных задач один за другим. Проблема в том, что я вызываю Task.WaitAll(задачу), замораживание пользовательского интерфейса. Как я могу выполнить следующую логику без зависания пользовательского интерфейса?

Ответы

Ответ 1

Это не цепочка задач.

Вам нужно выполнить цепочку задач с помощью ContinueWith. Последняя задача должна обновить пользовательский интерфейс.

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

Примечание Последняя строка имеет TaskScheduler.FromCurrentSynchronizationContext(), это обеспечит выполнение задачи в контексте синхронизации (поток пользовательского интерфейса).

Ответ 2

Лучший способ - использовать параллельную библиотеку задач (TPL) и Continuations. Продолжение не только позволяет вам создавать поток задач, но и обрабатывать ваши исключения. Это отличное введение в TPL. Но чтобы дать вам некоторую идею...

Вы можете запустить задачу TPL, используя

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

Теперь, чтобы запустить вторую задачу при завершении предшествующей задачи (по ошибке или успешно), вы можете использовать метод ContinueWith

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

Итак, как только task1 завершается, сбой или отменяется task2 'fires-up' и запускается. Обратите внимание, что если task1 завершилось до того, как вторая строка кода task2 была запланирована для немедленного выполнения. Аргумент antTask, переданный во вторую лямбду, является ссылкой на предшествующую задачу. См. эту ссылку для более подробных примеров...

Вы также можете передавать результаты продолжения антецедентной задачи

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

Примечание. Обязательно ознакомьтесь с обработкой исключений в первой ссылке, поскольку это может привести к тому, что новичок в TPL спустится.

Последнее, что нужно посмотреть, в частности, на то, что вы хотите, - это детские задачи. Ребенок - это те, которые создаются как AttachedToParent. В этом случае продолжение не будет выполняться до завершения всех дочерних задач

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

Надеюсь, это поможет.

Ответ 3

Вам нужно использовать континуции:

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