返回

像码农一样工作,理解同步异步、阻塞非阻塞IO

后端

同步、异步、阻塞、非阻塞与 IO 多路复用:深入解析

同步与异步

在编程中,同步异步 指的是代码执行的顺序。

  • 同步代码 :按照顺序执行,后面的代码必须等到前面的代码执行完成后才能执行。
  • 异步代码 :不按照顺序执行,后面的代码可以不等前面的代码执行完成后就执行。

举个例子:想象你在排队取咖啡。同步代码就好像你必须等到排在你前面的人拿到咖啡才能拿到自己的咖啡。而异步代码则相当于你可以在等待咖啡的同时去做其他事情,比如刷手机。

阻塞与非阻塞

阻塞非阻塞 指的是代码执行过程中是否会等待 IO 操作完成。

  • 阻塞代码 :在执行 IO 操作时会等待 IO 操作完成,后面的代码必须等到 IO 操作完成后才能执行。
  • 非阻塞代码 :在执行 IO 操作时不会等待 IO 操作完成,后面的代码可以不等 IO 操作完成就执行。

用咖啡店的例子来说,阻塞代码就像你站在柜台前等着咖啡煮好,而非阻塞代码则像你付完款后可以先去坐下来,等咖啡好了再过来拿。

IO 多路复用

IO 多路复用 是一种可以同时处理多个 IO 操作的技术。它通过一个 select 函数监听多个 IO 操作,当某个 IO 操作完成时,select 函数会通知应用程序,应用程序可以立即处理这个 IO 操作。

IO 多路复用在网络编程中非常常见,可以高效地处理大量并发连接。

同步异步、阻塞非阻塞的区分

同步异步和阻塞非阻塞是两个不同的概念:

  • 同步代码可以是阻塞的,也可以是非阻塞的。
  • 异步代码只能是非阻塞的。
  • 阻塞代码可以是同步的,也可以是异步的。
  • 非阻塞代码只能是异步的。

IO 多路复用是同步 IO 还是异步 IO?

IO 多路复用既可以是同步 IO,也可以是异步 IO。

  • 如果 IO 多路复用使用 select 函数,那么它是同步 IO,因为 select 函数会阻塞应用程序,直到某个 IO 操作完成。
  • 如果 IO 多路复用使用 epoll 函数,那么它是异步 IO,因为 epoll 函数不会阻塞应用程序,应用程序可以立即处理完成的 IO 操作。

有没有异步阻塞 IO?

没有异步阻塞 IO。异步代码只能是非阻塞的,所以异步阻塞 IO 是不存在的。

代码示例

以下是一个使用 epoll 实现 IO 多路复用的 Python 代码示例:

import selectors

selector = selectors.DefaultSelector()

def callback(key, events):
    print("Data received from", key.fileobj)

selector.register(sock, selectors.EVENT_READ, callback)

while True:
    events = selector.select()
    for key, _ in events:
        callback(key, key.events)

在这个例子中,我们创建一个 selector 对象,并向它注册一个套接字(sock)。当套接字有数据可读时,select 函数会触发 callback 函数,处理收到的数据。

总结

  • 同步异步指的是代码执行的顺序。
  • 阻塞非阻塞指的是代码执行过程中是否会等待 IO 操作完成。
  • IO 多路复用既可以是同步 IO,也可以是异步 IO。
  • 没有异步阻塞 IO。

常见问题解答

  1. 同步代码和异步代码哪个更好?
    取决于具体情况。同步代码通常更简单,但异步代码可以提高并发性。

  2. 阻塞代码和非阻塞代码哪个更好?
    取决于 IO 操作的类型。对于需要等待完成的 IO 操作(如文件读取),阻塞代码更合适。对于不需要等待的 IO 操作(如网络连接),非阻塞代码更合适。

  3. IO 多路复用和线程池有什么区别?
    IO 多路复用通过一个事件循环同时处理多个 IO 操作,而线程池通过创建多个线程同时处理多个任务。IO 多路复用通常比线程池更轻量级和高效。

  4. 为什么异步代码只能是非阻塞的?
    因为异步代码不等待 IO 操作完成,所以它必须是非阻塞的。

  5. 能不能举一个异步阻塞 IO 的例子?
    异步阻塞 IO 是不存在的。