两个进程同时打开旧报表再在旧 inode 上加锁,随后若其中一个执行 rename,另一个可能继续锁着已经脱离目标路径的 inode。save_report 因此锁定稳定的 DESTINATION.lock 旁路文件;持锁期间写临时文件并替换,退出时关闭锁句柄,却不删除锁文件。两个协作进程分别报告 SAVED\n、状态 0,最后的目标只与两个完整的报表金标之一相同;测试不假设哪个进程最后取得锁。

这不是全局互斥。反例持有 DESTINATION.lock 的排他锁,同时用普通 open '>' 覆写报表为 BYPASS\n;它仍可成功。Perl 5.38.2 pod/perlfunc.pod 的 flock 也注明锁通常是 advisory:不遵守协议的写入者不会被自动阻止。测试再由协作命令恢复金标,确认锁文件本身未被替换。

锁范围覆盖单次写入和 rename,不涵盖读取输入;每个进程先独立解析全部记录。对抗目录、符号链接或分布式文件系统的竞态与锁语义仍未测。练习:为了“清理临时文件”,顺便 unlink DESTINATION.lock 会怎样?下一进程可能锁住新建的另一 inode,互斥协议就断了。实测并发与绕过反例见 examples/perl-language/evidence/32-39.md、t/32-36-safe-report.t;只声称此 Linux 本机结果。

并行聚合也采用同一条“先完成、后发布”边界。LogTool::Course::Parallel::parallel_rows 把记录分片给受控数量的 fork worker,每个 worker 只回传自己的聚合结果;父进程等待全部 worker,任一 worker 非零或信号结束都返回 worker failed,publish_parallel_report 不调用 save_report。t/course-engineering.t 先写入 OLD\n,再注入一个失败 worker,断言旧文件逐字节不变;这验证的是父进程发布策略,不说明所有平台都有相同 fork 能力。