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

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

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

二、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 "异步返回结果";
    }
}

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

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

1、异步的传递性问题

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

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

本文转载于:https://www.jb51.net/aspnet/336985kou.htm 如有侵犯,请联系zhengruancom@outlook.com删除。
免责声明:正软商城发布此文仅为传递信息,不代表正软商城认同其观点或证实其描述。