把简单的事情做好

0%

关于异/同步,(非)阻塞

两者之间并无关系。

异步同步关注的是发生调用函数如何返回结果。同步就是立即返回结果或者等待返回结果,一有结果产生就回立刻返回。异步就是函数立刻返回,但是没有返回结果,等到结果产生的时候需要配合其他机制,比如事件,协程等将结果返回。

阻塞和非阻塞关注的是进/线程的状态。阻塞是当需要等待结果时,将进/线程挂起等结果产生以后返回。非阻塞就是不挂起进/线程,立刻返回错误(结果未产生)。

注意阻塞、挂起和睡眠的关系。进程阻塞是被动的行为,例如进程需要等待 I/O 完成,系统调度将其放到阻塞队列中,如果进程长时间没有被运行,就有可能被挂起。等到 I/O 完成后,系统调度将其重新加入到就绪队列中,等待 CPU 的执行。睡眠是进程主动的行为,进入到睡眠状态。

特定场景下的同步和异步

编程模型中的同步和异步

异步编程:全部通过事件机制、协程等编写或运行代码的方式,且代码不含同步阻塞的函数调用。一旦包含了同步阻塞的函数,异步模型就退化成同步模型。

同步编程:代码按照指令序列的顺序来执行,以及不属于异步编程的模型。

I/O 模型中的同步和异步

linux 有5大 I/O 模型,他们分别是

  • 阻塞式 I/O
  • 非阻塞式 I/O
  • I/O 复用(select 和 poll)
  • 信号驱动式 I/O
  • 异步 I/O

他们的简单定义和比较如下图

linux_io

其中,前面的四种 I/O 模型都属于同步 I/O。异步 I/O 在 linux 系统上是通过 aio 函数调用来实现的,即数据的等待阶段和从内核复制到用户空间的阶段都是由内核完成的,是真正的异步。

这里的同步和异步主要区分的是,内核通知应用程序是哪种 I/O 事件(就绪事件还是完成事件),以及是由谁来完成 I/O 读写(应用程序还是内核)

服务器并发模型中的同步和异步

并发模型中的同步和异步与编程模型中的定义相似,这里只列一下优缺点。

异步模型:通过事件机制实现的异步编程,会层层回调,难以维护。如果底层实现了协程调度,可以通过同步编程来实现异步编程的效果。

同步模型:目前大多数应用场景都是采用的这种编程模型,相对异步变成较为简单,调试方便,容易维护。

半同步/半异步模型:业务逻辑通过同步编程模型来实现,I/O 操作通过异步变成模型来实现。结合事件模型又产生了半同步/半反应堆等的变种。

其他

资源中的同步:为了维护数据的一致性,例如 mysql 的主从复制,线程间信号量和锁的实现,都是为了维护数据的一致性。

参考

怎样理解阻塞非阻塞与同步异步的区别-知乎
Linux高性能服务器编程
UNIX网络编程