# 运行锁用独占文件句柄,而不是命名互斥体 `Backup.ps1` 与 `Restore.ps1` 都会写 `manifest.json`,也都会在备份目录里用 `<归档>.tmp` 这个名字生成临时归档。计划任务与手动运行撞在一起时,两边会互相覆盖对方的账本、把彼此的 临时归档当成自己的。计划任务的 `-MultipleInstances IgnoreNew` 只挡住"计划任务之间", 挡不住手动运行,所以需要一把跨进程的锁。 用**独占文件句柄**(`FileShare.None` 打开 `Backups\.baknret.lock`)而不是命名互斥体: - 句柄由内核持有,进程被杀 / 崩溃时自动关闭,锁自动释放 —— 不会留下需要人工清理的陈旧锁; - 命名互斥体要跨会话(计划任务在另一个会话里跑,互斥体是会话局部的)就得用 `Global\` 前缀, 而那需要额外权限; - 文件系统的锁不区分会话与终端,计划任务与手动运行天然互相看见。 拿不到锁就**直接失败**(退出码 1 + 明确消息),不等待:单个条目压缩可能十几分钟,"等它跑完" 对用户来说和挂住没区别。锁文件里写明持有进程(pid / 起始时间 / 主机 / 用户)——"到底是谁 占着"这个问题不该靠猜。 只读模式不取锁(`-DryRun` / `-WhatIf` / `-VerifyOnly`):它们一个字节都不写。 代价:锁文件是备份目录里的一个额外文件(以 `.` 开头,归档枚举与孤儿审计都只看 `*.7z`, 不受影响);`Backups/` 被手工删除时锁也随之消失(这没关系,它本来就是运行期的)。