【问题标题】:async / await, how to use properlyasync/await,如何正确使用
【发布时间】:2020-08-23 06:57:33
【问题描述】:

我正在尝试学习 c# async/await,我找到的所有教程都是相同的东西,我真的什么都看不懂,例如,为什么这段代码不起作用?您能否解释一下异步/等待的方式和原因,或者提供一些有用的链接以真正理解,因为我没有这方面的经验。

class Program
    {
        static void Main(string[] args)
        {
            Waiter();
        }

        public static async void Waiter()
        {
            await HeavyStuff();
            Console.WriteLine("1");
        }

        public static async Task HeavyStuff()
        {
            await Task.Run(() => {
                Thread.Sleep(1000);
                Console.WriteLine("2");
            });
        }
    }

【问题讨论】:

标签: c# async-await


【解决方案1】:

在 Main() 中等待 Waiter()。否则它将调用 Waiter(),但 Main() 将继续执行而不等待 Waiter() 完成。这就是所谓的“一劳永逸”。 Main() 将完成,程序在 Waiter() 完成之前结束。

class Program
{
    static async Task Main(string[] args)
    {
        await Waiter();
    }

    public static async Task Waiter()
    {
        Console.WriteLine("1");
        await HeavyStuff();
        Console.WriteLine("4"); // this will be instantly shown after "3"
    }

    public static async Task HeavyStuff()
    {
        Console.WriteLine("2");  // this will be instantly shown after "1"
        await Task.Delay(1000);  // short version of Task.Run(() => Thread.Sleep(1000))
        Console.WriteLine("3");  // this will be shown after 1 second
    }
}

一个没有异步 Main 的火忘记版本。在这个版本中,我们阻止 Main 在 Waiter 完成之前完成(= 结束程序)。

class Program
{
    static void Main(string[] args)
    {
        Waiter();
        Thread.Sleep(2000);  // this gives Waiter() time to finish
    }

    public static async Task Waiter()
    {
        Console.WriteLine("1");
        await HeavyStuff();
        Console.WriteLine("4");
    }

    public static async Task HeavyStuff()
    {
        Console.WriteLine("2");
        await Task.Delay(1000);
        Console.WriteLine("3");
    }
}

【讨论】:

  • 一个重要说明。您确实将 Thread.Sleep 更改为 Task.Delay - 这是可以预期的。但是“HeavyStuff”方法中的形容词“Heavy”可能会产生极大的误导,我认为需要进一步解释。 “异步”不适用于“繁重”作业,例如您必须为大数字或类似的东西计算 PrimveNumber - 所以它不是为 CPU 密集型作业而设计的。它是为 IO I/O-bound 操作而设计的。当然,“一直异步”的规则会导致异步/等待几乎是“无处不在”(通常我们在查询数据库后会做一些事情)
  • 部分正确。当涉及到服务器等非 UI 程序时,您是绝对正确的。但是,当我们谈论带有 UI 线程的 UI 程序时,您实际上将 async await 与 Task.Run() 结合使用以从 UI 线程中摆脱繁重的工作。它不会使程序更快,实际上它会使程序变慢。但感觉更快,因为 UI-Thread 没有被阻塞,因此 UI 没有被冻结。
  • 完全同意。我并没有说你不能将两者结合起来:) 我只是在说明思维上的缺陷,那就是当你需要 HeavyStuff 的东西时——你带来 async/await (我在 async/await 的一开始就是这样想的——完全缺失要点)在下面的示例中,我将重命名为 IOBoundOperation 的方法 HeavyStuff - 以强调我们想要实现的目标。这是一个非常小的细节 - 但世界是由它们构建的;)
  • 附带说明,根据guidelines,异步方法应具有后缀Async。所以HeavyStuff 变成了HeavyStuffAsync 等等。
【解决方案2】:

我强烈建议您阅读 Stephen Cleary 撰写的 blog post,以获得对 async/await 意义的相当全面的解释。

但具体到您的问题,在方法签名中使用 async 会提示编译器创建状态机。您必须在方法中添加 async 关键字,以便在方法主体中启用 await(正如您在编写 Waiter()HeavyStuff() 时可能发现的那样)。

这里最终发生的是,当您从 Main 调用 Waiter 时,它确实调用了 Waiter 方法,但是因为您没有等待调用的结果,所以它会将其视为“即发即弃”。因此,您会体验到程序关闭。

同样,当 Waiter() 等待 HeavyStuff() 时(如果 Waiter() 是从 Main() 等待的),程序将等待该方法返回,然后在控制台上显示“1”。

上面的博客文章涵盖了这一点,但您可能不想从Waiter() 返回 void,因为这通常用于事件处理,实际上没有任何东西在等待方法的结果。虽然您没有使用方法返回值,但您正在等待它,因此最好改为返回 Task。

从 C# 7.1 开始,您可以在 Main() 入口点中使用 async 关键字,因此您的代码应如下所示:

class Program
{
    static async Task Main(string[] args)
    {
        await Waiter();
    }

    public static async Task Waiter()
    {
        await HeavyStuff();
        Console.WriteLine("1");
    }

    public static async Task HeavyStuff()
    {
        await Task.Delay(1000);
        Console.WriteLine("2");
    }
}

【讨论】:

  • 感谢链接和解释,您在写“但因为那不是同步方法”时是指“异步”吗?这是一个错字还是我又弄糊涂了
  • @MovsarBekaev 我已经改写了整个段落,希望能提供更好的解释。简而言之,通过不输入“等待”,您告诉程序不要等待它的结果并继续执行,从而关闭程序。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-03-28
  • 2022-01-07
  • 2013-08-14
相关资源
最近更新 更多