什么是同步与异步

同步、异步是一对,关注的是消息通知机制,一次完成是同步,两次完成是异步

  • 同步: 打电话(一次性把事情说完)
  • 异步: 发短信说事,等待回复(收到回复才算是把事情说完,这里是两次)

同步与异步是对应的,它们是线程之间的关系,两个线程之间要么是同步的,要么是异步的。

阻塞、非阻塞

阻塞、非阻塞是一对,关注的是程序在等待调用结果(消息,返回值)时的状态

  • 阻塞: 只能做一件事
  • 非阻塞: 做一件事同时可以做其他事

阻塞与非阻塞是对同一个线程来说的,在某个时刻,线程要么处于阻塞,要么处于非阻塞。

阻塞是使用同步机制的结果,非阻塞则是使用异步机制的结果。

同步异步和阻塞非阻塞的组合

同步、异步,与阻塞、非阻塞,没有必然联系

组合例子:

  • 同步阻塞: 打电话时候,其他任何事都做不了(比如不能同时看孩子)。效率低下
  • 同步非阻塞: 打电话时候,时不时看看孩子有没有危险。不停切换,效率低下
  • 异步阻塞: 发短信,在收到短信回复之前什么事都做不了(比如不能同时看孩子)。这种情形不常见
  • 异步非阻塞: 发短信,然后陪孩子玩,收到短信回复通知后再去看手机。效率最高

编码应用,指的都是用户代码逻辑,而非底层的实现:

  • 同步阻塞:最普遍最常见,符合思维,容易编写,容易调试
  • 同步非阻塞:自己的逻辑是同步,但由于使用了某个模块,而这个模块是实现异步的,所以立刻返回,于是从自己的代码逻辑角度看没有阻塞。

如果底层没有实现异步,那么上层的用户逻辑就不可能实现非阻塞。比如nodejs都说是单线程、异步、非阻塞I/O,其实是因为nodejs底层实现了异步(比如多线程之类的),但没有开放异步功能(比如多线程)给用户使用。

单线程、多线程

  • 单线程: 单线程只能实现同步,无法实现异步
  • 多线程: 是实现异步的最常用手段,实现异步还有多进程、协程等方法

进程、线程、协程


  • 进程:
    • 概念: 资源分配的基本单位
    • 通信: 进程之间的通信只能通过进程通信的方式进行
    • 多进程: 拷贝,使用fork(),生成子进程。每个进程拥有独立的地址空间(代码段、堆栈段、数据段)
  • 线程:
    • 概念: 调度运行的最小单位
    • 通信: 同一进程中的线程共享数据(比如全局变量,静态变量)
    • 多线程: 同一个进程中的线程,它们之间共享大部分数据,使用相同的地址空间。当然线程是拥有自己的局部变量和堆栈(注意不是堆)
  • 协程:
    • 概念: 非抢占式调度。用户态模拟进程线程的切换的具体实现,并非OS内核提供的功能。由程序员主动控制协程之间的切换。
    • 通信: 不要通过共享内存来通信,而应该通过通信来共享内存。

效率(即切换开销谁最小)比较:协程 > 多线程 > 多进程

编程难度比较: 协程 > 多进程 > 多线程。因为多线程要注意同步和互斥(线程安全),否则容易造成死锁。

并发、并行、多任务


  • 并发: (宏观的),逻辑上的同时运行,但同一时间底层可能只有一个任务在运行
  • 并行: (微观的),真正的同时运行,通过多核实现,任务分布在不同核上,也可以是分布在不同服务器上。
  • 多任务: 只要逻辑上能运行多个程序,就算是多任务,无论是否真正并行。

单核cpu就是并发,多核cpu才有可能并行,为什么说是有可能而不是绝对,因为系统上运行的程序数量必定比cpu数量多

只有底层实现异步,上层才有并行的可能,否则最多只是并发

cpu密集型和io密集型


  • io密集型: 包含网络io和磁盘io等,就是一个执行很久的操作不是由本机cpu进行操作而是发送一个请求然后由远程服务器操作,或者是发送一个读写磁盘的请求然后由磁盘硬件DMA进行操作。总之就是cpu发出一个请求,然后等待结果。如果一个业务的这种类型的操作比较多,就称这个业务为io密集型业务
  • cpu密集型: 一个操作由本机cpu进行运算。如果一个业务的cpu运算量比较大,而io操作比较少,则称这个业务为cpu密集型业务

多核处理器可以实现cpu运算的并行,提高运算能力,这个时候用多进程或者多线程就适用于cpu密集型。而协程不适合,因为协程是用户态的技术,只能单核,因此并不能提高cpu并行运算能力,也因此只适合io密集型。多进程和多线程除了在多核时候适应cpu密集型,也可以适用io密集型,尤其是单核时候,只不过协程的切换开销比进程和线程都小

协同式调度、抢占式调度

因为我们所熟悉的桌面操作系统,都是从协同式调度(如 Windows 3.2, Mac OS 9 等)过渡到抢占式多任务系统的。实际上,调度方式并无高下,完全取决于应用场景。抢占式系统允许操作系统剥夺进程执行权限,抢占控制流,因而天然适合服务器和图形操作系统,因为调度器可以优先保证对用户交互和网络事件的快速响应。当年 Windows 95 刚刚推出的时候,抢占式多任务就被作为一大买点大加宣传。协同式调度则等到进程时间片用完或系统调用时转移执行权限,因此适合实时或分时等等对运行时间有保障的系统。

协同式调度也叫非抢占式调度。协程就是经典的非抢占式调度。

事件驱动、观察者、生产者消费者、订阅发布、服务发现

下面几个术语,其实原理都差不多,都是一种主动式的操作,只是有的名词早出现,有的名词晚出现。

  • 事件驱动: 是一种宏观的经典模式,等待消息做事,就是事件驱动,下面的观察者、生产者消费者、订阅发布、服务发现都只是事件驱动的具体实现模型。(个人理解)

  • 观察者模式: 我认为这是事件驱动的具体模式,而下面的生产者消费者、订阅发布、服务发现,又是观察者模式的具体实现方式。

  • 生产消费者模式: 指的是由生产者将数据源源不断推送到消息中心,由不同的消费者从消息中心取出数据做自己的处理,在同一类别下,所有消费者拿到的都是同样的数据

  • 订阅发布模式: 本质上也是一种生产消费者模式,不同的是,由订阅者首先向消息中心指定自己对哪些数据感兴趣,发布者推送的数据经过消息中心后,每个订阅者拿到的仅仅是自己感兴趣的一组数据。

  • 服务注册与发现: 微服务架构由一个服务注册中心和围绕该注册中心的一堆服务组成。每个服务将自己的地址和其他元信息注册到注册中心(zookeeper或etcd等),有依赖它的其他服务会实时发现(watch机制)变化。

生产消费者模式和订阅发布模式是使用消息中间件时最常用的,用于功能解耦和分布式系统间的消息通信。