Shared Worker 通过串行调度和中心化执行避免多标签页磁盘写冲突,而非加锁;它统一封装写请求、FIFO队列执行、自动合并重复操作,并为IndexedDB、localStorage、文件系统提供针对性容错方案。

Shared Worker 本身不直接操作磁盘,所谓“磁盘高频锁死”实际是多个标签页并发写入同一本地资源(如 IndexedDB、localStorage 或通过 File System Access API 写文件)时引发的底层 I/O 竞争或事务阻塞。Shared Worker 的核心价值在于把这类高冲突操作收口到单一上下文,由它串行调度,从而从源头避免并发写入——不是靠“加锁”,而是靠“排队+中心化执行”。
为什么多标签页会触发磁盘写冲突
常见诱因包括:
- 多个页面同时调用
indexedDB.open()并发起写事务,尤其在低性能设备上易导致事务排队超时或 abort - 使用
localStorage.setItem()频繁更新同一 key,浏览器内部同步写入机制可能产生隐式锁等待 - 通过
window.showSaveFilePicker()+FileSystemFileHandle.createWritable()多次写入同一文件,底层文件系统(尤其 Windows NTFS 或某些 Linux mount 选项)对并发写敏感 - 未做节流的缓存持久化逻辑(如每次 API 响应都立即写 DB),在多标签页下被指数级放大
Shared Worker 如何收敛写入压力
关键不是让 Worker “代替页面写”,而是让所有写请求必须经由它,并强制顺序化:
-
统一入口封装:每个页面调用
worker.port.postMessage({ type: 'write', store: 'cache', key: 'user-data', value: obj }),而非自行打开 DB 或 setItem - 单队列串行执行:Worker 内部维护一个 FIFO 队列,每次只取一个任务执行;写完再发结果,再取下一个——天然消除并发
- 自动合并同类请求:对短时间内重复写同一 key 的请求(如 100ms 内多次更新 theme),Worker 可丢弃中间版本,只保留最后一次,减少实际 I/O 次数
- 失败隔离:某个页面传入非法数据导致事务失败,只 reject 对应请求,不影响后续任务;避免一个页面错误拖垮全站写入能力
针对不同存储目标的具体处理方式
IndexedDB:
Worker 中持有一个全局 db 实例(首次连接后复用),所有写请求走同一个数据库连接和事务。不为每个请求新开 transaction,而是复用或按需创建,且确保 transaction.oncomplete 后才处理队列下一项。
localStorage:
虽不推荐高频使用,但若必须,Worker 应将所有 set/get 请求转为异步队列操作,并用 postMessage 回传结果;避免页面直接调用同步 API 导致主线程卡顿。
文件系统写入:
页面先通过 showSaveFilePicker 获取 FileSystemFileHandle,序列化为 transferable handle(需 Chrome 94+ 支持),发送给 Worker;Worker 持有该 handle 并控制 createWritable() 和 write() 调用时机,确保同一文件句柄不被多线程并发写。
容错与降级必须做
Shared Worker 可能被浏览器终止(如长时间空闲),不能假设它永远存活:
- 页面端需监听
port.onmessage中的{ type: 'error', code: 'worker-died' },触发本地 fallback(如退化为单页 indexedDB 写) - Worker 自身应在
onconnect时检查是否已有活跃 DB 连接,无则重连;连接失败时广播状态,不静默失败 - 对非关键写操作(如 UI 状态缓存),可设超时(如 2s),超时未响应则跳过,防止整个队列被一个慢请求堵死

















