composer run-script 是单进程顺序执行的脚本调度器,不支持多线程、信号量或并发同步;所谓“信号量丢失”实为php posix信号量在cli模式下的资源泄漏问题,需手动清理,建议改用flock或redis锁。

Composer run-script 本身不涉及多线程,也不提供任何信号量(semaphore)或并发同步机制——它只是一个单进程、顺序执行的脚本调度器。所谓“多线程环境下信号量丢失”,是误将 Composer 的执行模型与操作系统级并发原语混淆了。
composer run-script 根本不跑在多线程里
Composer CLI 是纯 PHP 单进程应用,所有 scripts 都以子进程方式顺序启动(proc_open),不存在线程竞争、共享内存或信号量管理。你看到的“并发”现象(比如 CI 中多个 job 并行跑 composer test)是外部调度器(如 GitHub Actions runner)做的,不是 Composer 自身行为。
- 每个
composer run-script xxx调用都新建一个独立 PHP 进程,彼此隔离 - 没有跨进程锁、没有原子计数器、不维护全局状态
-
post-install-cmd这类钩子也只在单次 install 过程中触发一次,不会被多个实例同时调用
如果你真遇到“信号量丢失”,问题不在 Composer
典型场景:你在脚本里手动用了 sem_open / sem_acquire,但发现多次调用后信号量值异常归零或阻塞失效。这不是 Composer 的 bug,而是 PHP 的 POSIX 信号量在 CLI 模式下的固有限制:
围绕关键发现、作用机制、临床相关性及研究局限性展开讨论。适用于撰写或优化任何生物医学论文的“讨论(Discussion)”部分——包括结果解读、与既往文献关联、阐释意外发现、界定研究局限性,以及撰写结论。当用户输入以下任一指令时也会自动触发该功能: - “write my discussion” - “help me discuss my findings” - “how do I compare to prior studies” - “write the limitations par
- PHP 的
sem_*函数依赖系统 semaphores(如 Linux 的 System V IPC),而这些资源需显式清理,否则残留导致后续sem_open失败 - 子进程退出时若未调用
sem_close+sem_remove,信号量会“泄漏”,下次sem_open可能返回旧句柄但状态错乱 - Composer 不接管你的信号量生命周期——它只负责 fork + exec,之后完全交由你写的脚本自行管理
想安全用信号量?别在 scripts 里硬写
直接在 composer.json 的 scripts 字段里调用 sem_* 函数风险极高,因为:
- 脚本失败退出时无法保证
finally块执行,sem_remove极易漏掉 - CI 环境常设超时 kill -9,强制终止进程,IPC 资源彻底泄露
- 不同 OS 对 POSIX 信号量支持不一(macOS 已弃用,Docker 默认禁用 IPC namespace)
- 更稳妥的做法是改用文件锁(
flock)或 Redis 分布式锁——它们可自动释放或带 TTL
真正需要跨进程协调时,靠 Composer 脚本本身做不到。得换工具:用 redis-cli --eval 做原子计数,或把关键步骤抽成独立服务,由外部调度器统一管控。Composer 的职责只是装包和跑命令,不是做并发控制。










