Python内置库atexit匹配机制用双等号 绕过解释器退出即失效

【python】B站没人讲过的atexit,你知道是做什么的么?所有细节都知道么? — 码农高天

码农高天

atexit依托CPython生命周期在正常或异常退出时执行清理,但因注销采用相等性匹配易误删同值对象,且遭遇系统级os._exit调用会彻底失效。

AI-assisted summary of 【python】B站没人讲过的atexit,你知道是做什么的么?所有细节都知道么? — original source. Check the original for full context.

Source published:

Key ideas

  • atexit 虽是 Python 极精简的标准库,但其底层通过 C 语言实现,不仅支持函数注册传参,还因返回原函数对象而能直接作为装饰器使用。 (0:00)
  • atexit.unregister 采用相等性比较(==)而非身份比较(is)来匹配目标,且调用一次便会批量注销该函数的所有注册副本。 (2:37)
  • 模块内存在 _ncallbacks、_clear 和 _run_exitfuncs 等未公开内部接口,可用于统计注册量、强制清空或在进程退出前提前同步触发回调。 (5:00)
  • atexit 仅在解释器受控的生命周期(包括未捕获异常退出)内保证执行,若遭遇 os._exit 等绕过 Python 运行时的底层系统调用则会彻底失效。 (6:45)

Detailed notes

## Background 本视频由码农高天于 2023 年 10 月 22 日发布在 YouTube 平台。视频简介指出,本期主要探讨 Python 内置库中看似最简单的 atexit 模块,并深挖其中许多开发者未曾留意、值得讨论的底层细节。

## TL;DR 视频系统剖析了 Python 退出清理模块 atexit 的工作机制与隐藏特性。内容涵盖基础的 register 与 unregister 接口用法,并从 C 源码层面解释了为何 register 可以直接当作装饰器使用。高天重点演示了 unregister 是基于相等性(==)而非身份(is)进行匹配且会批量移除所有相同副本的反直觉细节,同时曝光了 _clear 等未记录在官方文档的私有接口。最后,视频界定了正常退出、未捕获异常与 os._exit 强退时的执行边界,并留下了 multiprocessing 子进程退出行为的悬念供观众思考。

## Atmosphere & style 整期内容兼具硬核技术拆解与极客式的风趣幽默,呈现出轻松严谨的编程探究氛围。高天在讲解中不断打趣自身“硬核 UP 主”的人设,借助生动简练的代码实验、源码翻阅以及构造自定义 __eq__ 实例一步步拨开机制迷雾。视频尾声更通过连环反转的悬念戛然而止,在让人会心一笑的同时充分激发了开发者的求知欲。

## Bulletin - [00:00](https://www.youtube.com/watch?v=bcr0lp8dDiU&t=0s) atexit 虽是 Python 极精简的标准库,但其底层通过 C 语言实现,不仅支持函数注册传参,还因返回原函数对象而能直接作为装饰器使用。 - [02:37](https://www.youtube.com/watch?v=bcr0lp8dDiU&t=157s) atexit.unregister 采用相等性比较(==)而非身份比较(is)来匹配目标,且调用一次便会批量注销该函数的所有注册副本。 - [05:00](https://www.youtube.com/watch?v=bcr0lp8dDiU&t=300s) 模块内存在 _ncallbacks、_clear 和 _run_exitfuncs 等未公开内部接口,可用于统计注册量、强制清空或在进程退出前提前同步触发回调。 - [06:45](https://www.youtube.com/watch?v=bcr0lp8dDiU&t=405s) atexit 仅在解释器受控的生命周期(包括未捕获异常退出)内保证执行,若遭遇 os._exit 等绕过 Python 运行时的底层系统调用则会彻底失效。

## Key takeaways - [00:00](https://www.youtube.com/watch?v=bcr0lp8dDiU&t=0s) atexit 模块核心公开接口仅有 register 和 unregister 两个函数,用于在 Python 进程退出生命周期中执行清理操作。 - [00:00](https://www.youtube.com/watch?v=bcr0lp8dDiU&t=0s) atexit.register 支持为回调函数传递任意位置参数与关键字参数,并将其存储在 C 语言底层的回调列表中。 - [00:00](https://www.youtube.com/watch?v=bcr0lp8dDiU&t=0s) CPython 源码中 register 函数在保存回调后会原样返回传入的函数指针,使其天然兼容 @atexit.register 装饰器语法。 - [02:37](https://www.youtube.com/watch?v=bcr0lp8dDiU&t=157s) 若一个函数被多次 register,只需调用一次 unregister 即可将其所有注册副本彻底注销,退出时不会触发任何一次。 - [02:37](https://www.youtube.com/watch?v=bcr0lp8dDiU&t=157s) 当传入未注册过的对象时,atexit.unregister 会保持静默无操作,避免抛出不必要的异常。 - [02:37](https://www.youtube.com/watch?v=bcr0lp8dDiU&t=157s) unregister 底层匹配已注册回调依据的是相等性比较(==)而非内存身份比较(is),自定义类的 __eq__ 实现会直接影响注销行为。 - [05:00](https://www.youtube.com/watch?v=bcr0lp8dDiU&t=300s) atexit 暴露了三个未在官方文档记录的私有接口:_ncallbacks、_clear 和 _run_exitfuncs,在部分调试分析工具(如 VizTracer)中有所应用。 - [05:00](https://www.youtube.com/watch?v=bcr0lp8dDiU&t=300s) 调用私有方法 _run_exitfuncs 会立即同步消耗并执行全部已注册的回调函数,并在执行完毕后自动清空注册列表。 - [06:45](https://www.youtube.com/watch?v=bcr0lp8dDiU&t=405s) 即使程序中途抛出未捕获的 Exception 导致异常退出,只要仍处于解释器受控生命周期内,atexit 回调依然能够稳定触发。 - [06:45](https://www.youtube.com/watch?v=bcr0lp8dDiU&t=405s) 通过 os._exit 强行终止进程会直接触发操作系统的系统调用瞬间杀死进程,导致 Python 解释器无法执行常规生命周期回收,atexit 完全失效。 - [06:45](https://www.youtube.com/watch?v=bcr0lp8dDiU&t=405s) 在 multiprocessing.Process 子进程中进行 atexit.register 注册时,其生命周期的退出与触发表现存在特殊的边界机制。

## Detailed walkthrough ### [00:00](https://www.youtube.com/watch?v=bcr0lp8dDiU&t=0s) atexit 的核心用法与底层注册机制

Python 内置的 `atexit` 模块几乎是所有标准库中最精简的一个,核心公开接口只有两个函数:`register` 和 `unregister`。它的功能顾名思义,就是在 Python 程序结束、进程准备退出时自动运行一些清理或回调代码。码农高天在开篇感叹道:“这个 module 很神奇的地方,在于我发现 B 站竟然没有一个视频是讲他的,不知道是因为这个 module 太简单了呀,还是太不常见了啊。”

```mermaid flowchart TD accTitle: atexit 注册与调用流程 accDescr: 展示函数通过 register 存储到 C 语言结构、通过 unregister 移除,并在进程退出或手动调用时执行的完整链路。

subgraph RegisterFlow ["注册阶段"] A["定义回调函数 / Callable"] -->|"atexit.register(func, *args)"| B["C 语言底层的回调列表"] A -.->|"当作装饰器 @atexit.register"| B end

subgraph TriggerFlow ["触发执行"] B -->|"正常退出或抛出未捕获异常"| C["进程退出时触发回调"] B -->|"调用 atexit._run_exitfuncs()"| D["立即运行并清空回调"] B -->|"atexit.unregister(func)"| E["按 == 匹配批量移除"] end ```

最基础的用法是定义一个普通的函数 `f`(例如内部执行 `print('exiting')`),随后调用 `atexit.register(f)`。当 Python 进程退出时,该函数便会自动触发。如果回调函数需要传参,也可以直接把参数依次追加在 `register` 后面,例如 `atexit.register(f, 'exiting')`。除此之外,`atexit.register` 还支持直接当作装饰器(decorator)使用,在定义 `f` 的上方写上 `@atexit.register` 即可达到完全相同的注册效果。

之所以能当作装饰器使用,本质在于 `register` 内部不仅执行注册,还会把传入的函数对象原样返回。翻看 CPython 底层源码可以发现,`atexit` 整个模块是用 C 语言编写的,其 `register` 函数在保存传入的 callback 函数与其调用参数后,在第 181 行将该函数指针原封不动地返回,从而完美兼容装饰器的语法糖。

- **基础注册**:`atexit.register(func, *args, **kwargs)`,注册需要在程序退出时执行的钩子。 - **装饰器语法**:`@atexit.register` 会就地注册函数并返回原函数对象。 - **注销接口**:`atexit.unregister(func)` 能够取消已注册的回调,注销后程序退出时将不再打印或执行任何关联代码。

---

[02:37](https://www.youtube.com/watch?v=bcr0lp8dDiU&t=157s) unregister 的深层行为:批量移除与相等性比较

对于一个看似简单的注销操作,官方文档与源码中藏着很多极易被忽略的细节。码农高天打趣称很多人可能会觉得简单的 API “一点都不符合我这个硬核 UP 主的身份”,随后深入拆解了 `unregister` 背后的两项核心行为:重复注册的处理与对象匹配机制。

当同一个函数被 `register` 注册了多次之后,只需调用一次 `atexit.unregister(f)`,底层就会直接移除所有与该函数匹配的注册条目,退出时一次都不会被调用。查阅 Python 官方文档可以明确看到三条规则: 1. 若要注销的函数此前未曾注册过,`unregister` 默默不做任何事,防止无谓的报错。 2. 若函数被注册了多次,该函数的**所有**副本都会被彻底移除。 3. 在比较待注销对象与已注册对象是否一致时,底层使用的是相等性比较(`==`)而非身份比较(`is`)。

为了演示 `==` 与 `is` 的本质区别,码农高天编写了一个自定义类 `MyFunc`。该类实现了 `__call__` 使其成为 callable 对象,同时实现了 `__eq__` 方法:只要传入的另一个对象同样是 `MyFunc` 的实例,二者即判定相等。 首先实例化 `f0 = MyFunc()` 并注册 `atexit.register(f0)`;随后实例化一个全新的对象 `f1 = MyFunc()`。显而易见,`f0` 与 `f1` 在内存中是两个独立的实例,`f0 is f1` 为 `False`,但因为重写了 `__eq__`,`f0 == f1` 判定为 `True`。此时执行 `atexit.unregister(f1)`,程序退出时 `f0` 居然被成功移除,控制台没有任何输出。对此,码农高天评价道:“这就是 Python 官方文档上说的用等于等于来比较,而不是 is 所造成的结果。那这件事当然就是你用对的是 feature,用错的是 bug 对吧。”

---

[05:00](https://www.youtube.com/watch?v=bcr0lp8dDiU&t=300s) 隐藏的无文档内部接口

除了公开文档中记载的 `register` 与 `unregister`,`atexit` 模块还暴露了几个未在官方文档中记录(Undocumented)的下划线内部接口。虽然未公开的私有接口通常提倡“能不用就不用”,但在部分复杂场景或性能分析工具中不可或缺,例如著名的代码追踪工具 VizTracer 在清理退出事件时、以及 Python 官方部分内置库的内部实现中都调用了这些方法。

这些暴露的隐藏接口共有三个: - `_ncallbacks()`:返回当前底层已注册的回调函数总数量。 - `_clear()`:强制清空所有已注册的回调列表。 - `_run_exitfuncs()`:立即按顺序同步执行所有已注册的回调,并且在执行完毕后自动将列表清空。

在演示中,代码首先注册了一个打印 `exiting` 的函数,随后调用 `atexit._ncallbacks()` 输出返回值 `1`;接着调用 `atexit._clear()` 后,程序正常退出时果然不再打印任何内容。另一个对比实验是在退出前显式调用 `atexit._run_exitfuncs()`,其后紧接着一行普通的代码 `print('before exit')`。常规流程下本应是主流程先打出 `before exit`,退出生命周期时才由 hook 打印 `exiting`;但此时输出顺序颠倒,控制台先打出了 `exiting`,随后才打出 `before exit`,证明 `_run_exitfuncs()` 会立刻消耗并执行掉所有的注册函数。

---

[06:45](https://www.youtube.com/watch?v=bcr0lp8dDiU&t=405s) atexit 的执行保证边界与未解谜题

很多开发者高度依赖 `atexit` 进行关键资源回收或日志落盘,那么它究竟是否百分之百保证会被执行?码农高天直接给出了定性结论:“当你的 Python 程序 life cycle 完成了的时候它永远会运行。”无论是正常执行到代码末尾,还是中途未捕获异常抛出了未处理的 exception 导致进程终止,这都在 Python 解释器的掌控范围之内,Python 退出时必然会逐一调用 `atexit` 注册的函数。

然而,一旦脱离了 Python 运行时的受控生命周期,`atexit` 就会彻底失效。最典型的代表便是 `os._exit(0)`。在注册了函数之后如果直接调用 `os._exit(0)`,退出时屏幕上空无一物。这是因为 `os._exit` 是直接陷入操作系统的系统调用(system call),它会瞬间杀死当前进程,导致 Python 根本没有机会按照自己的节奏做常规的退出回收。

在视频尾声,码农高天留下了一个极具思考价值的“连环反转”悬念。他给出了如下一段多进程代码:在一个独立函数中通过 `atexit.register` 注册了一个打印 `exiting` 的动作,然后利用标准库 `multiprocessing.Process` 创建并启动这个子进程。他向观众提问:此时这段代码运行的结果是什么?背后真正的原因又是什么?随后便以幽默的方式戛然而止:“好那如果你以为这期视频到这里就结束的话,那就又错了……那这期视频就到这里,希望你们都猜对了这期视频结束的时间,如果喜欢的话记得点一下关注,别忘了一键三连哦。哎,还有个问题没说,下期视频再说吧。”

- **生命周期覆盖**:正常执行结束、未捕获 Exception,`atexit` 都会稳定触发。 - **失效场景**:调用底层的 `os._exit()` 等系统级强制终止时,绕过了解释器流程,`atexit` 完全失效。 - **课后思考题**:在 `multiprocessing.Process` 子进程内部进行 `atexit.register` 注册,其退出行为与回调触发是否受影响。

## Mentioned 码农高天, Python, CPython, atexit, B站, VizTracer, C语言, multiprocessing

## Video description 这期视频聊一下atexit这个库。这可能是python的内置库里最简单的一个,但是里面也有很多可以讨论的细节。我相信几乎所有人都能从这个视频里收获点自己之前不知道的知识~

[Source](https://www.youtube.com/watch?v=bcr0lp8dDiU)

中文简报

Python内置库atexit匹配机制用双等号 绕过解释器退出即失效 atexit依托CPython生命周期在正常或异常退出时执行清理,但因注销采用相等性匹配易误删同值对象,且遭遇系统级os._exit调用会彻底失效。 视频系统剖析了 Python 退出清理模块 atexit 的工作机制与隐藏特性。内容涵盖基础的 register 与 unregister 接口用法,并从 C 源码层面解释了为何 register 可以直接当作装饰器使用。高天重点演示了 unregister 是基于相等性(==)而非身份(is)进行匹配且会批量移除所有相同副本的反直觉细节,同时曝光了 _clear 等未记录在官方文档的私有接口。最后,视频界定了正常退出、未捕获异常与 os._exit 强退时的执行边界,并留下了 multiprocessing 子进程退出行为的悬念供观众思考。 atexit 虽是 Python 极精简的标准库,但其底层通过 C 语言实现,不仅支持函数注册传参,还因返回原函数对象而能直接作为装饰器使用。 (0:00) atexit.unregister 采用相等性比较(==)而非身份比较(is)来匹配目标,且调用一次便会批量注销该函数的所有注册副本。 (2:37) 模块内存在 _ncallbacks、_clear 和 _run_exitfuncs 等未公开内部接口,可用于统计注册量、强制清空或在进程退出前提前同步触发回调。 (5:00) atexit 仅在解释器受控的生命周期(包括未捕获异常退出)内保证执行,若遭遇 os._exit 等绕过 Python 运行时的底层系统调用则会彻底失效。 (6:45)

Reading summary

码农高天系统解析了 Python 内置模块 atexit 的工作机制与底层实现,指出该模块不仅提供基础的退出回调注册,还在注销逻辑、隐藏内部接口以及解释器生命周期边界上存在极易被忽视的技术细节。 核心注册机制与装饰器支持 (0:00) Python 标准库中的 atexit 模块核心公开接口仅有 register 与 unregister 两个函数,用于在 Python 进程退出时自动执行清理代码。基础用法支持向 register 传入回调函数及可选参数,同时也支持直接作为装饰器 @atexit.register 使用。根据 CPython 底层 C 语言源码(如第 181 行),register 在将回调函数及其参数保存到底层结构后,会将传入的函数指针原样返回,从而天然兼容装饰器语法。 注销逻辑的相等性比较与批量移除 (2:37) 官方文档与源码表明,atexit.unregister 包含三条关键规则:注销未注册过的函数时静默忽略;若同一函数被重复注册多次,调用一次 unregister 会将其所有副本全部彻底移除;底层在匹配待注销对象时采用相等性比较(==)而非身份比较(is)。 为验证对象比对机制,码农高天通过自定义类 MyFunc 演示了这一特性:该类重写了 __eq__,使相同类的不同实例均判定相等。当实例化 f0 并注册后,通过独立的实例 f1 执行 atexit.unregister(f1),由于 f0 == f1 为 True,已注册的 f0 会被成功移除并在退出时不输出任何内容。这表明利用相等性匹配既可以成为便利特性,也可能因不恰当的重写引入隐蔽缺陷。 未公开的内部调试与控制接口 (5:00) 除公开接口外,atexit 模块还暴露了三个官方未记载(Undocumented)的下划线私有接口:_ncallbacks() 用于返回当前已注册的回调函数总数;_clear() 用于强制清空所有已注册回调;_run_exitfuncs() 则会立即按顺序同步执行所有注册的回调并在执行后清空列表。 这些接口在普通业务开发中通常不建议使用,但在 VizTracer 等复杂性能分析工具及 Python 官方内置库底层有实际应用。实验表明,在退出前显式调用 atexit._run_exitfuncs() 会立刻消耗并触发回调,导致回调的执行时机先于后续的普通打印语句,改变常规生命周期顺序。 解释器生命周期与执行保证边界 (6:45) 码农高天指出,只要 Python 程序的生命周期正常完成,无论是执行至代码末尾还是抛出未捕获的异常终止,处于解释器受控范围内的回调均会可靠执行。然而,一旦脱离 Python 运行时的受控生命周期,atexit 将彻底失效。例如直接调用 os._exit(0) 会陷入操作系统级系统调用并瞬间终止进程,导致解释器失去执行退出清理的时机。 视频最终留下了一个未解疑问:若在 multiprocessing.Process 子进程中通过 atexit.register 注册退出回调,子进程的退出行为与回调触发规则将受到何种影响,该问题留待后续内容解答。

Original transcript

[0:00] 这期视频给大家介绍一个python内置的module 叫做app exit这个猫很神奇的地方 在我发现B站竟然没有一个视频是讲他的不知道是因为这个猫猪太简单了呀 还是太不常见了啊那AXED这个module确实是python所有的内置的module里面几乎最简单的一个它只有两个函数分别是register跟unregister而且它的功能很好理解但是呢我们在使用的过程中也确实有一些不容易察觉的小细节我们这次争取一期视频 让你把这个module完全搞明白那at exit module顾名思义就是可以让你在python退出的时候去运行一些代码举个例子我们定义了一个函数F然后这个F就是print的一个exiting我们用add access.register把这个函数注册到ad exit里当我们想在这个python程序结束的时候 也就是这个进程退出的时候 运行某一些函数 我们就用app.exit.register 然后把这个函数给放进去我们运行一下可以看到他打印了这个exiting那如果这个函数需要一些参数的话比如说现在F需要一个S这个参数那么我们就需要在register里面把这个参数也传进去比如像现在这样我们传进去了一个函数名f还传进去一个参数exiting我们再跑一下这个程序可以看到结果跟之前是一样的那按ExitDiregister也可以当作一个Decorator来用把它放在F's definition前面是一样的效果我们尝试一下可以看到结果是一样的为什么呢 我们之前学过decorator的等价形式 对不对所以你可以知道第三行这个decorator就相当于这段代码我们看这样是不是就很好理解了这个写法是不是跟刚才的写法是一样的只不过你现在知道了,AdExit的Register还会返回拿到的参数函数那我们看一下这原码啊and exit这个modium是用c写的那么Register这个函数就在这儿它其实也并不复杂它就是把每一个你传取的callback都存起来了大家看在这就是把函数和argument都保存起来然后未来就可以用在最后181行这里他把这个函数返回了回来这样decorator就可以正常工作了好 那如果我们程序进行到一半我不想在程序结束的时候再跑这个函数了我们就用按Register我们看在左边第7行我们把这个f函数给做了一个unregister我们再运行一下这项代码哎可以看到他就没有print任何东西了好 那到目前为止我们讲的东西都很容易对吧而且看起来我们已经把ad exit这个module里面所有东西都讲完了你可能会觉得这一点都不符合我这个硬核up主的身份对吧首先我也做很多这个面向初学者的内容其次我们这个视频还没有结束我们先聊这个unregister,这个unregister是怎么工作的呢?如果我们注册了多个函数他怎么unregister呢?我们可以试一下我们看左边我们注册了三次f函数然后我们只做了一次unregister你猜猜它会打印出来东西吗结果是不会啊 我们来python的官网看一下unregister这个函数的说明啊第一他说如果这个function没有register他就什么都不做这是防止一些无谓的报错也就是你可以尽情的做unregister第二 如果 function 被register了不止一次那么所有的方神都会被移除这就是我们刚才举的例子第三 这件事也很有趣 当它比较两个函数是否一致的时候 它用的是等于等于而不是 is这是什么意思呢我们看一下这个例子啊当你直接定义函数的时候呢那件事不重要但是如果我注册的是一个object是一个callable比如说像我定义的这个myfunk一样我定义了一个它的call method 它就是callable了然后我还定义了一下它的eq method就是如果另外一个也是myfunk instance的话我就认为他们俩是相等的好 我们建立一个ColourableObject F0然后我们register F0看一下结果可以看到这段代码跟我们刚才那段代码效果是一样的我们register的是一个callable object了 接下来有趣的事儿来了啊 我们建立了一个新的myfunc object叫f1 那f1跟f0是两个object 他们相等但不是同一个东西我现在按register F1我们运行下程序可以看到这个F0实际上是被unregister掉了这就是python官方文档上说的用等于等于来比较 而不是is所造成的结果那这件事当然就是你用对的是feature 用错的是bug对吧 [5:00] 就是尽量深入的跟大家聊一下让你以后留个心眼好 那如果你以为我这次终于讲完了 那你就又错了在python的官方文档上虽然只介绍了两个函数但是AppAxys实际上还有一些暴露了接口但是没有Document的函数对于这类的函数 肯定是能不用就不用 然而有的时候我们确实要用比如说Vistracer里面 由于它要处理一些程序结束时候的事 所以它就要用到这几个函数那python的内置库里实际上也用到了它这些函数我们在未来可能有机会会讲到好我们看在右边一共有三个新的内容啊一个是clear 一个是ncallbacks 还有一个是runexitfox这三个东西从名字你基本上就可以知道他们是干嘛的n cobbets就是有几个已经注册的函数clear就是把这些东西都清空run exit func就是把这些函数现在就运行好 我们看左边的例子 第13行打印N callbacks然后第14行把他clear掉之前的这个函数是可以打出来exiting的对不对我们现在运行一下程序可以看到他打印出来了一个1这是第13行的这个N callbacks然后他就没有运行这个Exiting了说明第14行我们把这个东西clear掉了我们把它变换一下 第14号我们做一个run exit fox然后第15我们print一个东西在我们没有做 run exit funx的时候应该是先print before exit然后再靠这个hook print-existing那这段程序呢 我们运行一下可以看到它先打了这个exiting 然后才打before exit就是这个run_edit_funks会把register的函数运行一遍然后clear掉好 那如果你以为这期视频就到这里了 那你就又错了我们还要讲一个很重要的东西啊就是这个at exit 它永远好使吗就是是不是能保证每一次你的python程序退出的时候他都可以把你reddit这个函数给运行了这个对于很多人来说是一个非常重要的feature对不对我们在这里直接告诉大家结论当你的python程序life cycle完成了的时候它永远会运行什么意思呢 就是不管你的程序正常结束了 还是raise了一个exception那这些都是在python的控制范围之内所以python都可以调用这个app exit里面注册的函数 那有没有它不好使的时候呢当然有还记不记得上期视频我们聊过一个OS.exit我们看左边在register了F0之后我们运行了OS的下向下 exit 0可以看到这个程序运行的时候就没有成功的打印出exiting了原因我们之前也说过os.exit是一个system call它会导致python没有办法正常的按照它自己的节奏退出好那如果你以为这期视频到这里就结束的话那就又错了我们看左边的代码 我定义了一个函数 这个函数会register一个print exiting然后我把这个函数放到multiprocessing的process里面执行请问结果是什么为什么好那这期视频就到这里希望你们都猜对了这期视频结束的时间如果喜欢的话记得点一下关注别忘了一键三连哦哎 还有个问题没说下期视频再说吧字幕志愿者 杨茜茜