youyichannel

志于道,据于德,依于仁,游于艺!

0%

一次性捋清楚:同步、异步、阻塞、非阻塞

每次谈到网络 I/O 的时候,总会出现几个容易令人混淆的概念:同步 / 异步、阻塞 / 非阻塞 的区别。

让我们来盘一盘这几个概念!

同步 / 异步

同步和异步指的是:当前线程是否需要等待方法调用执行完成。

【栗子🌰】调用一个烧水的方法 f:

1)同步指的是调用这个方法 f,当前线程需要等待水烧完,然后得到结果,接着再继续执行剩下的代码逻辑。

// 同步调用
result = f();
if(result) {
// do other things
other();
}
// other task
continue_task();

2)异步指的是调用这个方法 f,立马就返回,不需要等待水烧完,可以立即执行后续的代码逻辑,然后利用回调或者事件通知的方式得到水烧开的结果。

// 异步调用
f(() => {
// callback
other();
});
// other task
continue_task();

可以很直观的看出,同步和异步就是调用方式的不同

需要注意的是,在异步调用的情况下,代码逻辑相对不太直观,通常都需要通过回调方法或者事件通知,在复杂逻辑下对开发者的要求较高。同步调用代码逻辑相对比较直观,等待执行完毕后拿到结果紧接着执行后续的逻辑,不容易出错。

很多方法虽然是异步调用的方式,但是最终的使用还是「异步转同步」。

比如在 Java 中,向线程池提交一个任务,得到一个 future 对象,此时是异步的,然后调用了 future.get(),那么就变成了同步等待这个任务执行完成。

阻塞 / 非阻塞

阻塞和非阻塞指的是:当前接口数据还没有准备就绪,线程是否被阻塞挂起。

阻塞挂起:当前线程还拥有 CPU 时间片,调用了阻塞的方法,由于数据未准备就绪,当前线程就染出 CPU。

阻塞和同步看起来都是在等待,但是它们本质不一样,同步没有让出 CPU。

非阻塞就是当前接口数据还未准备就绪时,线程不会被阻塞挂起,可以不断轮询请求接口,看看数据是否已经准备就绪。

小结

1)同步和异步:当数据还未处理完成时,代码的逻辑处理方式不同;

2)阻塞和非阻塞:当数据还未处理完成时,线程的状态。

从 I/O 的角度来看

  • 阻塞和非阻塞指的是发起 I/O 请求后,用户线程状态的不同,阻塞I/O在数据未准备就绪的时候会阻塞当前用户线程,而非阻塞 I/O 会立马返回一个结果,不会阻塞当前用户线程。
  • 同步和异步是指,内核的 I/O 拷贝实现,当数据准备就绪后,需要将内核空间的数据拷贝至用户空间,如果是同步 I/O 那么用户线程会等待拷贝的完成,而异步 I/O则这个拷贝过程用户线程可以处理其他任务,当内核拷贝完成之后会「通知」用户线程。