我的延续任务在完成运行之前似乎已经完成 选项#1-展开选项2-避免创建新任务选项#3-AttachToParent

问题描述

我认为延续任务有一个非直觉性的问题。我会等待延续任务,但是在任务完成运行之前会产生RanToCompletion吗?这是简短的源代码输出如下:

private static void TestChildTasks()
{
    Task t = Task.Run(() => RunParentTask());
    Task t2 = t.ContinueWith(task => Task.Run(() => runchildTask()));
    //Task t2 = Task.Run(() => runchildTask());
    Console.WriteLine("Waiting on t1");
    t.Wait();
    Console.WriteLine("Done waiting on t1");

    Console.WriteLine($"Waiting on t2,status of {t2.Status}");
    t2.Wait();

    Console.WriteLine($"Finished; child task is {t2.Status}");
}

private static void RunParentTask()
{
    Console.WriteLine("Parent Task is running");
    Thread.Sleep(2000);
    Console.WriteLine("Parent Task is done");
}

private static void runchildTask()
{
    Console.WriteLine("Child task is running");
    Thread.Sleep(3000);
    Console.WriteLine("Child Task is done");
}

以下是输出

Waiting on t1

Parent Task is running

Parent Task is done

Done waiting on t1

Waiting on t2,status of Running

Finished; child task is RanToCompletion

press enter to exit

Child task is running

Child Task is done

为什么子任务返回状态RanToCompletion后仍继续运行?

解决方法

让我们看一下下面的行:

Task t2 = t.ContinueWith(task => Task.Run(() => RunChildTask()));

尽管您已将t2声明为Task,但实际上它是Task<Task>

Task<Task> t2 = t.ContinueWith(task => Task.Run(() => RunChildTask()));

为什么?因为Task.Run创建了一个新的Task

您有几种解决方法:

选项#1-展开

要从Task中创建Task<Task>,您需要调用Unwrap方法。

Task t2 = t.ContinueWith(task => Task.Run(() => RunChildTask())).Unwrap();

进行此修改后,输出将如下所示:

Waiting on t1
Parent Task is running
Parent Task is done
Done waiting on t1
Child task is running
Waiting on t2,status of WaitingForActivation
Child Task is done
Finished; child task is RanToCompletion

选项2-避免创建新任务

实际上,您可以不使用RunChildTask来呼叫Task.Run

Task t2 = t.ContinueWith(_ => RunChildTask());

进行此修改后,输出将如下所示:

Waiting on t1
Parent Task is running
Parent Task is done
Done waiting on t1
Child task is running
Waiting on t2,status of Running
Child Task is done
Finished; child task is RanToCompletion

选项#3-AttachToParent

如果愿意,您不仅可以将Task附加到ContinueWith,还可以附加Task.Factory.StartNew

Task t2 = null;
Task t = Task.Factory.StartNew(() =>
{
    RunParentTask();
    t2 = Task.Factory.StartNew(RunChildTask,TaskCreationOptions.AttachedToParent);
});

进行此修改后,输出将如下所示:

Waiting on t1
Parent Task is running
Parent Task is done
Child task is running
Child Task is done
Done waiting on t1
Waiting on t2,status of RanToCompletion
Finished; child task is RanToCompletion

还有其他几种方法。