繁体   English   中英

等待任务完成,在“等待”之后进行操作

[英]await on task to finish, with operations after the 'await'

我知道我可能会被否决,但显然我对 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 只是不同 function 上实际等待的占位符。

打印“2”

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

output 是

Hello World
1
Finished
2

怎么了?

当您等待t (这是GenerateTask方法中的res )时,您正在等待创建的 Task 而不是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;
}

output 现在是:

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

看起来这是因为 new Task 的构造函数只采用某种形式的 Action (因此即使它是异步的,Task 也永远不会返回)。 所以本质上你正在做的是你的委托的异步无效。 您的await Task.Delay(10000)正在返回,并且该操作被视为“完成”。

如果将await Task.Delay(10000)更改为 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()

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

您应该使用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()] 不理解异步委托。 这实际上与您想要使用 StartNew 的原因中的第 1 点相同。 问题在于,当您将异步委托传递给 StartNew 时,很自然地会假设返回的任务代表该委托。 但是,由于 StartNew 不理解异步委托,因此该任务实际代表的只是该委托的开始。 这是编码人员在异步代码中使用 StartNew 时遇到的第一个陷阱。

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM