任务未完成就开始继续执行

9
我在C#,VS2012,WPF 4.5中有以下代码。 我的期望是,在任务完全完成后执行.ContinueWith(这是延续的整个目的,不是吗?)。 这应该导致finalResult的值为2。
int myTestInt = 0;

Task task = Task.Factory.StartNew(async () =>
{
   myTestInt = 1;
   await Task.Delay(TimeSpan.FromSeconds(6));
   myTestInt = 2;

}).ContinueWith(_ =>
   {
      int finalResult = myTestInt;
   });

事实上,finalResult 被赋值为1。因此似乎续集已经在 await 语句中开始了。
这是预期的行为吗?我有什么遗漏吗?我不能依赖于 ContinueWith 在任务完全完成后才开始吗?
更新:
Justin 的回答激发了我检查以下内容:
int myTestInt = 0;
Task task=Task.Factory.StartNew(async () =>
{
   myTestInt = 1;
   await Task.Delay(TimeSpan.FromSeconds(6));
   myTestInt = 2;
});

task.Wait();
int result2 = myTestInt;

finalResult仍然被设置为1。没有可靠的方法等待包含await的任务完成吗?


4
我认为你应该直接调用 Task.Delay 方法,而不使用 await 关键字。 - Ioannis Karadimas
2
你会注意到_实际上是一个Task<Task>,这完全支持Justin所说的。 - Marc Gravell
实际上,使用Task.Delay(6)将产生预期的结果,但不会等待6秒钟。我认为删除整行代码也差不多。 - Frank im Wald
3个回答

10

当你将一个async委托传递给Task.Factory.StartNew时,返回的Task仅代表该委托的第一部分(直到它await某些尚未完成的内容)。

然而,如果你将一个async委托传递给新的Task.Run方法(出于这个原因而被包含),返回的Task表示整个委托。所以你可以像预期一样使用ContinueWith。(虽然await通常比ContinueWith更好)。

关于StartNewRun的更多信息,请参见Stephen Toub关于此主题的文章


4

使用await会立即将控制权返回给调用函数,这里是你的任务的StartNew。这意味着任务将完成并执行ContinueWith。 如果你真的希望在执行ContinueWith之前任务完成,请不要使用await Task.Delay。


我认为你是对的 - await标记当前任务已完成。虽然从语义角度来看并非如此。由于await是当今所有软件的基本构建块,这意味着不再依赖于ContinueWith。你不觉得这是个bug吗? - Frank im Wald

-1

我在MSDN上看到了这个::-)

public async  void button1_Click(object sender, EventArgs e)
{
    pictureBox1.Image = await Task.Run(async() =>
    {
        using(Bitmap bmp1 = await DownloadFirstImageAsync())
        using(Bitmap bmp2 = await DownloadSecondImageAsync())
        return Mashup(bmp1, bmp2);
    });
}

所以不要忘记 "async()" !!!


他的代码使用了 async,但问题在于他正在使用 TaskFactory,而该类没有重载方法可以接受 Func<Task>Func<Task<T>>,而 Task.Run 则有这样的方法。 - Scott Chamberlain

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接