“两个线程只读同一格时,完全不交的堆模型又过于严格,需要 分数权限 或其他共享只读代数。锁保护状态则需资源不变式,在取得锁后暂时打开、释放前关闭。经典 CSL 也不自动建模弱内存重排、无锁算法…”
形式陈述 ​
在简化的有理份额模型中,断言
同一位置的碎片必须同意当前值;份额总和不能超过
读写规则把这一区别写进前置条件。读操作可以保持任意正份额:
写操作则消费并恢复完整权限:
只要资源账本始终可组合,就不存在一个合法写者与另一个合法读者同时访问同一位置。
有理数加法只是教学模型。更一般的 share algebra 可以表示不可数拆分、树形份额或非简单数值的权限,并把组合定义为部分交换运算。无论表示如何,可靠性的关键都是:两个可同时存在的非完整份额不能各自授权冲突写,而所有碎片重新汇合后可以恢复独占能力。
直觉
完整权限像原件,能读、改和销毁;把它撕成两张可验证的半份凭证后,两位持有人都能确认内容,却没有任何一人能单独改动原件。只有把所有凭证收回,写权限才恢复。这里的分数不是“这个线程大概拥有一半内存”,而是可精确守恒的逻辑能力。
这种设计填补了纯堆不交模型的空隙。只读查表、并行遍历和共享不可变配置不需要强迫只有一个线程持有地址;同时,写者必须等待所有读份额归还,因而不会与读者并发修改同一位置。
例子与边界
初始资源为
把两份分别交给线程 A、B。二者都能读取并验证结果为
若只收回 A 的一半,持有者总份额仍是
拆分不必只有两份。例如三个工作线程可各得
允许任意正份额写会立刻破坏规则:A、B 各拿
推论与应用
分数权限支持读写锁的规格:读锁取得一个非零份额,多个读者的份额可共存;写锁必须取得完整份额,因而排除所有读者与其他写者。fork/join 并行循环可在 fork 时拆分数组元素的读权限,在 join 时重新合并,再进入写阶段。
份额记录的是访问权,不是程序中有多少指针别名,也不保证持有者最终归还。带借用、线程取消或异步回调的语言需要说明份额怎样跨作用域移动;自动权限推断若返回 unknown,也不能把缺少证明当成没有竞争。
份额还可附在递归数据结构的不同层次:遍历者拥有每个节点的读份额,维护线程只有在回收整条路径的全部份额后才能旋转或释放节点。这里的算术守恒是安全证书,不是运行时引用计数;实现可以完全擦除这些分数,只要类型或逻辑证明确保相同访问纪律。
参考资料
- John Boyland, “Checking Interference with Fractional Permissions,” Static Analysis Symposium, LNCS 2694, 2003, pp. 55–72。
- Richard Bornat, Cristiano Calcagno, Peter O’Hearn, and Matthew Parkinson, “Permission Accounting in Separation Logic,” POPL, 2005, pp. 259–270。
- John Boyland, “Semantics of Fractional Permissions with Nesting,” ACM TOPLAS 32(3), 2010, Article 6。