商城首页欢迎来到中国正版软件门户

您的位置: 首页 > 文章列表 > 编程开发 > 谈谈对.NET中async/await的理解

谈谈对.NET中async/await的理解

  发布于2026-07-23 阅读(0)

扫一扫,手机访问

一、什么是同步?什么是异步?

在 .NET 的世界里,asyncawait 这两个关键字可以说是异步编程的门面担当。async 用来标记一个方法是异步方法,里面可以藏着多个 await 表达式;而 await 呢,就是专门在异步方法里等待某个任务(Task 或 Task)完成的。当程序执行到 await 时,当前方法会暂停,但主线程并不会傻等着——它会转身去干别的事,等那个任务完成了再回来继续跑剩下的代码。

那同步和异步到底怎么区分?

  • 同步:一个方法被调用,调用者必须老老实实等到它执行完,才能接着往下走。这就好比排队买奶茶,前面的人不取走,后面的人就干等着。
  • 异步:方法一被调用就立即返回,然后启动一个线程(Task)去处理它内部的逻辑,调用者根本不用等。这就相当于你点了外卖,然后该干嘛干嘛,外卖做好了自然会送到你手上。

二、async/await 是怎么提高性能的?

异步最大的好处就是非阻塞——调用线程不会停下来干等子线程完成。所以,那些不急着要结果、但是很耗时的任务,完全可以扔到异步里去跑,整个程序的效率自然就上来了。

举个直观的例子:主线程要依次执行 5 个方法,耗时分别是 0.1s、0.2s、0.3s、0.4s、0.5s。如果用同步的方式,主线程得老老实实一个一个串着跑,总时间就是 1.5 秒。但如果把这些方法都改成异步的,主线程大概只需要 0.5 秒就能搞定。

原因很简单:同步就像一根线穿五颗珠子,一颗穿完才能穿下一颗,总时间就是所有珠子时间之和。异步呢,相当于五个人每人拿一根线同时穿一颗珠子,总时间取决于最慢的那颗珠子——0.5 秒。因为主线程调用异步方法时,不会一头扎进去执行,而是在遇到 await 后立刻返回一个 Task,这个 Task 会在子线程里慢慢跑,主线程则继续往后走,去调用下一个方法。

那问题来了:主线程跑得飞快,异步方法的结果怎么收回来?常见的方式有这么几种:

1. 直接用 await 等待

public class Demo
{
    public async Task DemoAsync()
    {
        await Task.Delay(1000);
        Console.WriteLine("1秒后执行");
    }
}
public Class Program
{
    static async Task Main(string[] args)
    {
        Demo demo = new Demo();
        // 使用 await 等待DemoAsync的执行
        await demo.DemoAsync();
        Console.ReadKey();
    }
}

2. 使用事件通知

internal class Program
{
    static void Main(string[] args)
    {
        Demo demo = new Demo();
        demo.OnEvent += (() =>
        {
            Console.WriteLine("事件订阅");
        });
        demo.DemoAsync();
        Console.ReadKey();
    }
}
public class Demo
{
    public event Action OnEvent;
    public async Task DemoAsync()
    {
        Console.WriteLine("开始执行");
        await Task.Delay(1000);
        Console.WriteLine("1秒后执行");
        OnEvent?.Invoke();
    }
}

3. 回调函数

internal class Program
{
    static void Main(string[] args)
    {
        Demo demo = new Demo();
        demo.DemoAsync(() =>
        {
            Console.WriteLine("回调函数");
        });
        Console.ReadKey();
    }
}
public class Demo
{
    public async Task DemoAsync(Action callback)
    {
        Console.WriteLine("开始执行");
        await Task.Delay(1000);
        callback?.Invoke();
        Console.WriteLine("1秒后执行");
    }
}

4. 只启动异步操作,不等待结果

如果只是启动一个异步任务,根本不需要它的返回值,那就直接调用,不用加 await。主线程不会被阻塞,也不会去管结果。

internal class Program
{
    static void Main(string[] args)
    {
        Demo demo = new Demo();
        demo.DemoAsync();
        Console.ReadKey();
    }
}
public class Demo
{
    public async Task DemoAsync()
    {
        Console.WriteLine("开始执行");
        await Task.Delay(1000);
        Console.WriteLine("1秒后执行");
    }
}

5. 把异步结果存到变量里,后面再取

如果想在某个时刻拿到异步结果,但又不想立刻等它完成,可以先把返回的 Task 存起来,等需要的时候再用 Wait().Result 来取。

internal class Program
{
    static void Main(string[] args)
    {
        Demo demo = new Demo();
        var demoResult = demo.DemoAsync();
        // 使用 wait等待异步的完成
        demoResult.Wait();
        if (demoResult.IsCompleted)
        {
            Console.WriteLine("str执行完成");
            Console.WriteLine(demoResult.Result);
        }
        Console.ReadKey();
    }
}
public class Demo
{
    public async Task DemoAsync()
    {
        Console.WriteLine("开始执行");
        await Task.Delay(1000);
        Console.WriteLine("1秒后执行");
        return "异步返回结果";
    }
}

三、异步到底解决了什么?到底起到了什么样的作用?

  • 提高响应性:用 asyncawait 可以避免那些长时间运行的操作(比如 IO 操作)把主线程卡死,应用程序的反应自然更快了。
  • 简化异步代码:写异步代码现在几乎和写同步代码一样直白,出错概率也大大降低。
  • 优化资源使用:线程在等待任务完成时会被释放出来,跑去服务别的任务,而不是傻等——资源利用率一下就上去了。

四、在使用异步时的一些问题的解决

1、异步的传递性问题

异步的效果会沿着调用链一路向上传染。你调了个异步方法,那调用它的方法也得变成异步,而且返回类型还得改成 Task 或 Task。这就有点像病毒传播,一发不可收拾。

那怎么避免这种“强制传染”呢?有几种思路:

  • 不用 await 直接调用:如果不需要异步方法的结果,可以直接调用,不加 await,这样调用者就不用变成异步方法了。
  • 用 Task.Run 来启动异步操作Task.Run 会新起一个任务去跑异步逻辑,从而切断了传递性。
  • 用事件或回调来处理结果:如果只需要在完成时得到通知,事件或回调比直接 await 更灵活,也不会逼迫上层变成异步。
  • 把异步结果存起来,后面再等:将 Task 赋值给一个变量,等需要结果时再调用 Wait() 或访问 .Result,虽然不优雅但确实能避开传递性。
本文转载于:https://www.jb51.net/aspnet/336985kou.htm 如有侵犯,请联系zhengruancom@outlook.com删除。
免责声明:正软商城发布此文仅为传递信息,不代表正软商城认同其观点或证实其描述。

热门关注