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

这是 UI 线程的运行。我需要一个接一个地执行任务变量中的所有任务。问题是如果我调用 Task.WaitAll(task),UI 会冻结。如何在不冻结 UI 的情况下执行以下逻辑?

4

3 回答 3

32

这不是任务链。

您需要使用ContinueWith. 最后一个任务需要更新 UI。

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

注意最后一行,TaskScheduler.FromCurrentSynchronizationContext()这将确保任务将在同步上下文(UI 线程)中运行。

于 2012-07-25T12:26:54.507 回答
16

最好的方法是使用Task Parallel Library (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“启动”并开始运行。请注意,如果task1在到达第二行代码之前已经完成,task2将被安排立即执行。antTask传递给第二个 lambda的参数是对前面任务的引用。有关更详细的示例,请参阅此链接...

您还可以传递先前任务的延续结果

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

我希望这有帮助。

于 2012-07-25T12:29:33.307 回答
5

您需要使用延续:

lastTask.ContinueWith(() => newTask.Start());
于 2012-07-25T12:26:13.583 回答