atexit注册函数仅在当前进程正常退出时触发,不跨进程生效;子进程崩溃或被信号终止时,其atexit回调不会执行,必须用try/except/finally或signal.signal显式处理清理。

子进程崩溃时主进程不退出?atexit 失效的真相
Python 的 atexit 注册函数只在当前进程正常退出(如 sys.exit()、脚本自然结束)时触发,**不会跨进程生效**。多进程场景下,子进程崩溃或被 os.kill() 终止,其内部注册的 atexit 回调根本不会执行。
常见错误现象:你在子进程中用 atexit.register(cleanup_tmp),但子进程被 signal.SIGTERM 杀掉后,临时文件还在;或者子进程因未捕获异常直接退出,atexit 彻底静默。
- 真正可靠的清理必须放在子进程自己的异常处理链路里,比如
try/except/finally块中 - 若依赖信号退出,需显式注册
signal.signal(signal.SIGTERM, handler)并在 handler 里调用清理逻辑 -
multiprocessing.Process的terminate()方法不给子进程任何执行清理的机会,它等价于os.kill(..., SIGKILL)
multiprocessing.Pool 关闭后资源没释放?别只调 close()
Pool.close() 只是禁止提交新任务,已提交任务仍在运行;Pool.terminate() 强制杀掉所有工作进程,但两者都不等待子进程真正退出——这意味着主进程可能提前结束,而子进程残留的文件句柄、锁、共享内存段还卡在系统里。
使用场景:跑完一批 CPU 密集任务后要立刻释放 GPU 显存、删掉临时 mmap 文件、关闭数据库连接池。
立即学习“Python免费学习笔记(深入)”;
无论从何种情形出发,在目前校长负责制的制度安排下,中小学校长作为学校的领导者、管理者和教育者,其管理水平对于学校发展的重要性都是不言而喻的。从这个角度看,建立科学的校长绩效评价体系以及拥有相对应的评估手段和工具,有利于教育行政机关针对校长的管理实践全过程及其结果进行测定与衡量,做出价值判断和评估,从而有利于强化学校教学管理,提升教学质量,并衍生带来校长转变管理观念,提升自身综合管理素质。
- 必须配对使用:
pool.close(); pool.join()才能确保所有子进程结束 -
pool.join()会阻塞主进程,直到所有工作进程退出;若子进程卡死,主进程就永远 hang 住 - 更安全的做法是加超时:
pool.join(timeout=30),超时后手动pool.terminate()再重试 - 注意:
join()前必须先close()或terminate(),否则抛ValueError
子进程里用 threading.Lock 会死锁?共享对象的陷阱
多进程不是多线程,threading.Lock、threading.Event 等线程同步原语**不能跨进程共享**。把它们作为参数传给 Process 或放进 Manager,实际发生的是 pickle + 反序列化,得到的是新对象副本,完全无法协调多个进程。
常见错误现象:多个子进程同时写一个日志文件,以为加了 threading.Lock 就安全,结果日志还是乱序或损坏;或者用 Manager().dict() 存状态,却用线程锁去保护它,纯属无效操作。
- 进程间同步必须用
multiprocessing.Lock、multiprocessing.Semaphore、multiprocessing.Event -
Manager()返回的对象(如dict、list)本身已做进程安全封装,一般不需要额外加锁 - 如果要用文件做协调,优先考虑
os.open(..., os.O_EXCL | os.O_CREAT)这类原子操作,而不是靠锁
Windows 下子进程继承句柄导致主进程无法退出
Windows 默认将主进程的打开句柄(文件、socket、pipe)**继承给子进程**。如果子进程没显式关闭这些句柄,主进程调用 process.join() 或 pool.close() 后,仍可能因句柄被占用而无法彻底退出——尤其常见于日志文件、数据库连接、子进程启动的 subprocess。
性能影响:句柄泄漏会拖慢进程销毁速度,严重时触发系统句柄数限制,后续 Process 创建失败,报错 OSError: [WinError 87] The parameter is incorrect。
- 启动子进程时加
close_fds=True(Python 3.7+ 默认为True,但旧版本或某些封装库可能关掉) - 子进程入口函数开头加
import multiprocessing; multiprocessing.set_start_method('spawn', force=True),避免fork类行为残留 - 检查是否用了
subprocess.Popen(..., stdout=xxx)把主进程的sys.stdout传下去——这会让子进程持有一个指向主进程 stdout 的句柄









