异步编程-阻塞&非阻塞

异步是必须系统提供并行或者并发的能力才能做到的,没有并行或者并发的系统不支持异步编程

所谓同步和异步,本质上区别是:逻辑请求发出后,是否需要等待结果,才能继续执行操作

是多个逻辑结构的编程设计模式,异步实现的依赖

多任务在同一个时刻同时执行,必须要求多个执行主体,比如多核CPU或者多CPU,每个核心独立执行一个任务,多个任务同时执行,不需要切换

并行一定是同时运行,系统提供的能力才能使用并行

是多个逻辑结构的编程设计模式,异步实现的依赖

多个任务在同一个时间段内同时执行,有资源就按调度执行,中间会进行切换,切换方式可以是抢占,也可以是阈值控制等等

并发并不一定是要同时执行,所以并行是并发的一个子集,并发强调的是系统具有处理多个任务的能力,但不一定要同时

逻辑调用方式,程序函数期望的描述

所谓同步,就是发出一个功能调用时,在没有得到结果之前,该调用就不返回或继续执行后续操作。简单来说,同步就是必须一件一件事做,等前一件做完了才能做下一件事

逻辑调用方式,程序函数期望的描述

异步与同步相对,当一个异步过程调用发出后,调用者在没有得到结果之前,就可以继续执行后续操作。当这个调用完成后,一般通过状态、通知和回调来通知调用者

对于异步调用,调用的返回并不受调用者控制

逻辑调用结果,程序函数调用后的状态描述

阻塞调用是指调用结果返回之前,当前 进程/线程 会被挂起,调用 进程/线程 只有在得到结果之后才会返回,并被唤醒

逻辑调用结果,程序函数调用后的状态描述

调用立刻返回,不会导致挂起

这里的立刻返回,其实程序本身还是可能被唤醒的,只是间隔很短,可以忽略

根据上面的概念,可以组合 4 种异步编程情况

直接入口 main 函数无脑访问阻塞函数即可

当阻塞时,当前程序会释放 cpu 和 资源,让相关的程序去执行

绝大多数程序的入口都是这种写法,一个 main 函数 阻塞循环,既让程序存活,也不占用必要的资源

例如 C++ Rust 默认多线程 开发就这个模型

linux 的 epoll 这种 IO 多路复用,也是经典的同步调用,非阻塞的

这种模型,需要调用者 轮询去查看调用结果,总体资源占用不大,调整轮询间隔,甚至可以达到性能和资源占用的微妙的平衡

异步阻塞的目的就是更高的性能,更低延迟

能够让长耗时的任务安排到独立 进程/线程运行,达到更好的性能

不过现代计算机体系,异步支持得非常好了,使用 并发或者 并行,都能高效利用资源

比如 游戏 gameplay 主逻辑 就这么这么实现的

带有 GC 的编程语言,合理使用异步阻塞,可以指标上降低 GC ,原理是降低内存的创建和销毁,强行占用CPU来提高性能表现,稍微提高的 CPU 占用了反而是好处

通过调用 异步函数,埋一个回调函数,来接收异步函数的实际处理结果

其实这种方式是理论上最吃资源的,稍微思考一下就知道,都不等着不释放资源,肯定忙啊

这也同时是最优压榨系统资源的方式