关于事件循环的 15 个问题

如果你的程序中只有一个事件循环,没有其他代码,那么是否有可能在同一时间运行两行代码?

不可能。

一个事件循环中的所有代码都在一个操作系统线程中运行,所以任何时刻只能有一段代码在运行。

在一个有事件循环的程序中,是否可以有其他线程?

是的。

例如,在 node.js 中,所有的 Javascript 代码都在一个线程中运行,但还有其他工作线程来处理网络请求和其他 I/O。

在一个事件循环中,是否由操作系统来负责调度函数执行的顺序?

不是。

例如,用Python的 asyncio,做调度的代码是一个 Python 程序。

事件循环真的是一个循环吗?(就像像 for 循环或 while 循环?)

是的。

通常事件循环都是以while循环的形式实现的,它看起来像这样。

1
2
3
4
while True:
self._run_once()
if self._stopping:
break

(以上是 Python 的 asyncio 事件循环的实际代码)

事件循环如何决定下一个函数的运行?

通过队列。

当函数准备好运行时,它们会被推到队列中,然后事件循环按顺序执行队列中的函数。

如果一个网络请求返回,并且它有一个附加的回调,该回调是否会被推送到事件循环的队列中?

是的。

当网络请求或其他 I/O 完成后,或者用户点击了某些东西,或者因为该函数计划在那时运行等,函数可能会被推入事件循环的队列中。

常规函数和异步函数一样吗?

不一样。

异步函数特殊之处在于,它们可以被“暂停”并在稍后的事件循环中重新启动。

例如,在下边这段 Javascript 代码中

1
2
3
4
5
async function panda() {
let x = 3;
await elephant();
let y = 4;
}

事件循环调度 elephant(),暂停 panda,并在 elephant() 运行完毕后调度 panda() 重新启动。普通的非 async 函数不能像这样暂停和重启。这些可以暂停和重启的异步函数的另一个名字是协程。

如果你要求事件循环在某个时间运行一个函数(比如 Javascript 中的setTimeout),它能保证在那个时间运行吗?

不能。

事件循环会尽力而为,但有时会延迟。

在 Javascript 中,promises、setTimeout、async/await 和回调是否都使用相同的事件循环?

是的。

虽然它们的语法不同,但它们是安排代码稍后运行的不同方式。

在下边这段代码中,是否可以让事件循环在x=3后中断,然后运行别的东西?

1
2
x = 3;
y = 4;

不能。

你需要显式让步给事件循环,让它运行一个不同的函数,例如使用 await

如果你运行一些CPU密集型的代码,如下,事件循环最终会中断代码吗?

1
2
3
while(true) { 
i = i * 3
}

不会。

通常,你可以通过运行一些 CPU 密集型操作来长时间阻塞事件循环。

如果你的Web 服务器事件循环中 CPU 的使用率到达100%,当新的 HTTP 请求进来时,是否能够立即响应?

不能。

如果你的事件循环的 CPU 总是很忙,那么新进来的事件就不会得到及时处理。

Javascript代码总是有一个事件循环吗?

是的。

至少在 node.js 和浏览器中是这样的,Javascript 代码总是有一个事件循环运行。

是否存在一个所有的事件循环都在使用标准的事件循环库?

没有。

在不同的编程语言中,有很多不同的事件循环实现。

你可以在任何编程语言中使用事件循环吗?

可以。

大多数编程语言并没有像 Javascript 那样的“一切都在事件循环中运行”的模式,但许多语言都有事件循环库。而且从理论上讲,即使还没有自己语言的事件循环库,你也可以编写一个。