我知道我可能会被否决,但显然我只是不太了解 async-await,我发现的问题/答案以及我发现的文章都没有帮助我找到这个问题的答案:
>我怎样才能打印出“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 只是现实生活中等待不同函数的占位符。
答案 0 :(得分:6)
实际打印的是 2,打印 1 后 10 秒。如果在打印“完成”后添加 Console.ReadLine();
,您可以观察到这一点。
输出是
Hello World
1
Finished
2
当您等待 t
(即 res
方法中的 GenerateTask
)时,您正在等待创建的任务,而不是 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");
}
答案 1 :(得分:4)
看起来是因为 new Task 的构造函数只采用某种形式的 Action(因此即使 Task 是异步的,它也永远不会返回)。所以基本上你正在做的是与你的委托的 Async void 。您的 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
替换为我写的内容应该可以满足您的需求。 async/await 将把你的方法变成一个已经开始执行的任务。这正是您想要做的,所以我不太确定您对编辑的要求。
从 GenerateTask
返回的任务可以随时等待,也可以根本不等待。您几乎不需要执行 new Task()
。我能想到的唯一原因是,如果您想将任务的执行推迟到以后,但有更好的方法来解决它,而不是调用 new Task()
。
如果您使用我在现实生活中展示的方式,请告诉我哪些方法无效,我将很乐意提供帮助。
答案 2 :(得分:3)
您应该使用 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's blog post,我引用了其中的内容:
<块引用>[Task.Factory.StartNew()] 不理解异步委托。这实际上与 您希望使用 StartNew 的原因中的第 1 点。问题 是当您将异步委托传递给 StartNew 时,很自然地 假设返回的任务代表该委托。然而,由于 StartNew 不了解异步委托,该任务实际上是什么 代表只是该委托的开始。这是其中之一 程序员在异步中使用 StartNew 时遇到的第一个陷阱 代码。
这些注释也适用于 Task
构造函数,它也不理解异步委托。
但是,重要的是要注意,如果您已经在代码中等待并且不需要并行化一些计算绑定的代码,则根本不需要创建新任务 - 只需使用中的代码您自己的 Task.Run()
即可。