等待任务完成,操作在"等待"之后完成



我知道我可能会被否决,但显然我只是不够了解异步等待,我找到的问题/答案以及我找到的文章并没有帮助我找到这个问题的答案:

如何打印出"2"?或者实际上,为什么 2 没有被打印出来,无论是在 await t 还是在 t.Wait() 中?

static Task t;
public static async void Main()
{
Console.WriteLine("Hello World");
t = GenerateTask();     
await t;
//t.Wait();
Console.WriteLine("Finished");
}
public static Task GenerateTask()
{
var res = new Task(async () => 
{
Console.WriteLine("1");
await Task.Delay(10000);
Console.WriteLine("2"); 
});
res.Start();
return res;
}

编辑:我正在创建一个任务并返回它,因为在现实生活中我需要稍后从不同的方法等待此任务。

Edit2:await Task.Delay只是现实生活中不同函数的占位符。

打印 '2'

实际打印 2,打印 1 后 10 秒。如果您在打印"完成"后添加Console.ReadLine();,则可以观察到这一点。

输出为

Hello World
1
Finished
2

发生了什么事情?

当您等待t(GenerateTask方法中res)时,您正在等待创建的任务,而不是res创建的任务。

如何修复(花哨的方式)

您将需要等待外部任务和内部任务。为了能够等待内部任务,您需要公开它。要公开它,您需要将任务的类型从Task更改为Task<Task>,并将返回类型从Task更改为Task<Task>

它可能看起来像这样:

public static async Task Main()
{
Console.WriteLine("Hello World");
var outerTask = GenerateTask();     
var innerTask = await outerTask; // what you have 
await innerTask;                 // extra await
Console.WriteLine("Finished");
Console.ReadLine();
}
public static Task<Task> GenerateTask() // returns Task<Task>, not Task
{
var res = new Task<Task>(async () => // creates Task<Task>, not Task
{
Console.WriteLine("1");
await Task.Delay(TimeSpan.FromSeconds(10));
Console.WriteLine("2"); 
});
res.Start();
return res;
}

现在的输出是:

Hello World
1
2
Finished

如何修复(简单方法)

不需要外部任务。

public static async Task Main()
{
Console.WriteLine("Hello World");
var t  = GenerateTask();     
await t;
Console.WriteLine("Finished");
Console.ReadLine();
}
public static async Task GenerateTask()
{
Console.WriteLine("1");
await Task.Delay(TimeSpan.FromSeconds(10));
Console.WriteLine("2"); 
}

看起来这是因为新任务的构造函数只采用某种形式的操作(所以即使它是异步的,任务也永远不会返回)。所以本质上你正在做的是与你的代表的异步空白。您的await Task.Delay(10000)正在返回,操作被视为"完成"。

如果将await Task.Delay(10000)更改为Task.Delay(10000).Wait()并从委托中删除异步,则可以看到这一点。

另一方面,我以前从未亲自见过或使用过新任务。Task.Run()是一种更标准的方法,它将允许使用 await。也意味着您不必自己调用 Start()。

您可能已经知道这一点,但在这种特定情况下,您根本不需要新任务。你可以这样做:

public static async Task GenerateTask()
{
Console.WriteLine("1");
await Task.Delay(10000);
Console.WriteLine("2");
}

关于您的编辑

用我写的东西替换你的GenerateTask应该做你想做的事。异步/等待会将您的方法转换为已开始执行的任务。这正是您要做的,所以我不太确定您对编辑的要求。

GenerateTask返回的任务可以随时等待,也可以根本不等待。你几乎永远不需要做new Task()。我能想到的唯一原因是,如果您想将任务的执行推迟到以后,但是有更好的解决方法而不是调用new Task()

如果您使用我在现实生活中展示的方式,请告诉我什么不起作用,我将很乐意提供帮助。

你应该使用Task.Run()而不是直接创建一个Task

public static Task GenerateTask()
{
return Task.Run(async () =>
{
Console.WriteLine("1");
await Task.Delay(10000);
Console.WriteLine("2");
});
}

Task.Start()不起作用,因为它不理解异步委托,返回的任务仅表示任务的开始。

请注意,出于同样的原因,您也无法通过使用Task.Factory.StartNew()来解决此问题。

请参阅Stephen Cleary关于这个问题的博客文章,我引用其中:

[Task.Factory.StartNew()] 不理解异步委托。这实际上与 第 1 点,说明为什么要使用 StartNew。问题所在 是当你将异步委托传递给 StartNew 时,很自然地 假定返回的任务表示该委托。然而,由于 StartNew 不理解异步委托,该任务实际上是什么 代表只是该委托的开始。这是其中之一 编码人员在异步中使用 StartNew 时遇到的第一个陷阱 法典。

这些注释也适用于Task构造函数,该构造函数也不理解异步委托。

但是,请务必注意,如果您已经在代码中等待并且不需要并行化某些计算绑定代码,则根本不需要创建新任务 - 只需单独使用Task.Run()中的代码即可。

最新更新