💻 第四章 程序像河水一样流动着
📄 一句话概括

事件驱动是另一种特殊流程:程序不再从头跑到尾,而是守着事件队列,来一个事件转一圈——界面、服务器、游戏都是这个形态。

📝 本节和前面几节的关系

上一节的【中断】是硬件层的插入流程,这一节的【事件驱动】是它的软件亲兄弟——两种特殊流程合起来收尾第四章。

一、事件驱动是什么

传统程序【像河水顺流】:从第一行跑到最后一行,顺序由程序自己定。 事件驱动程序【像值班员】:平时待命,事件(鼠标点击、键盘敲击、消息到达)来了才动一下,处理完回去继续待命。 核心是一个事件循环:不停从事件队列取事件 -> 分派给对应的处理程序 -> 处理完再取下一个。

二、与中断的血缘关系

两者是同一思想的两次出场:中断是硬件层的【打到手上再处理】,事件是软件层的【排队等着被取走】。 很多系统的底层就是这样连的:硬件中断发生时,系统把【这件事】包装成一个事件塞进队列,事件循环再慢慢分发——中断负责通知,事件负责处理。

三、谁在用事件驱动

界面程序:按钮等点击、窗口等关闭——不能写死顺序,用户爱点哪点哪; 服务器:等请求到达,来一个处理一个;游戏:等按键、等定时器。 【⚠️ 2026 现状】事件驱动已成现代应用的主流形态:前端的 addEventListener、Node.js 的事件循环、手机 App 的生命周期回调,全是这个模型。

四、动手 Demo:事件循环转三圈

模拟一个事件队列,看事件循环如何取一个、处理一个、再取下一个。

# 事件循环: 取事件 -> 分派 -> 再取
queue = [("点击按钮", "onClick"), ("敲键盘", "onKey"),
         ("关窗口", "onClose")]
while queue:
    ev, handler = queue.pop(0)
    print("取到事件:", ev, "-> 交给", handler)
print("队列空 -> 继续待命")

实跑输出:

--------------------------------------------------------------------
事件驱动——不再单线跑,而是等消息
--------------------------------------------------------------------

传统程序:从头往下执行(像河水顺流)。
事件驱动:程序【待命】,事件来了才动一下——这是 GUI 与现代应用的主流形态。

运行时是这样一圈圈转的(事件循环):

    [第 1 圈] 从事件队列取出: 鼠标点击按钮 -> 交给 onClick 处理 处理
    [第 2 圈] 从事件队列取出: 键盘敲入字母 -> 交给 onKey 处理 处理
    [第 3 圈] 从事件队列取出: 窗口关闭 -> 交给 onClose 处理 处理

    ... 队列空了就继续等,一来新事件就再转一圈 ...

  对比             顺序执行             事件驱动
  ---------------------------------------------------------
  谁决定下一步     程序自己             外部事件
  数据从哪来       程序里写死的顺序     事件到达的先后
  典型场景         批处理、计算         界面、服务器、游戏

事件驱动与中断是同一种思想的两次出场:
中断是硬件层的【打到手上再处理】,事件是软件层的【排队等着被取走】。

事件顺序由【到达先后】决定,不是程序写死的——这正是事件驱动与顺序执行的根本差别。 队列空了程序也不退出,只是【待命】——界面程序能一直开着,靠的就是这个循环不停转。

30 秒实验

  1. 在浏览器控制台给一个按钮绑 addEventListener,点一下看回调触发——亲手喂一个事件。
  2. 对比中断与事件:各写一句它们的分工(中断负责通知、事件负责排队处理)。

常见误区

  1. 以为事件驱动是【多线程】——单线程也能事件驱动(一个循环轮流处理),两者不是一回事。
  2. 以为事件处理完程序就结束——事件循环是【永动机】,队列空了就待命,不是退出。
自测三问(点开对答案) 1. 事件驱动的核心机制是什么? —— 事件循环:不停从事件队列取事件、分派给处理程序、处理完再取。 2. 事件驱动与中断是什么关系? —— 同一思想的两次出场:中断是硬件层通知,事件是软件层排队处理。 3. 哪些程序适合事件驱动? —— 界面、服务器、游戏——顺序不由程序定,而由外部事件定的场景。

相关笔记