
假设一个配置值初始为0,任务要求先读取现状,再改成1,最后读回新值。三个工具调用依次到达:先读、写入、后读。写工具已经进入队列,执行期间也确实没有读工具同时工作,最后一次读取却仍然拿到0。
这个现象容易让人先去查文件缓存或锁。但如果后一次读取在写工具真正启动之前就完成了,旧值完全符合当时的状态。写入时守住独占,并不能追回已经发生的读取。
本地 cc-python 的两个调度器,恰好能把这个区别拆开。同样的三次调用交给流式执行器,后读可以先于排队的写入;交给按连续安全工具分组的批次执行器,后读则会等写入完成。
要解释这件事,关键是区分两道边界:写入开始后有没有其他工具同时运行,写入排队后还能不能有后来者先启动。它们建立在不同的时刻,也解决不同的问题。

Edit 入队时,后来的 Read 仍可启动
流式执行器收到一个完整工具调用后,会查询它的 is_concurrency_safe 标记。在这份代码里,文件读取工具返回真,文件编辑工具沿用基类默认的假。调度器据此决定立即启动,还是暂存到队列。
安全工具能够立即启动,还需要满足一个条件:当前没有独占工具正在执行。判断使用的是 _has_exclusive_running。条件满足时,执行器创建任务,并把调用ID与任务放进 _pending。
非安全工具到达时,只会进入 _queue。此时没有把独占标记改成真,也没有单独记录“已有一个先到的写入正在等待,后来的安全工具不能越过它”。排队与正在独占,在状态上是两回事。
于是,先读到达后可以启动;写入到达后留在队列;后读接着到达,看见仍然没有独占工具正在运行,也可以启动。它不会因为前面已经排着一个写工具,就自动停在写工具后面。
队列里的写入要等到 get_results 才被处理。处理非安全工具时,执行器先等待已启动任务结束,再设置独占标记,启动写入,等它完成后清除标记。对当前例子来说,后读已经属于那批需要先等完的任务。
这里的安全标记表达的是调度许可:这个工具可以进入并发执行的集合。它没有表达“本次读取必须依赖上一项编辑完成”。同一个只读工具,有时读取互不相关的资料,有时是在检查刚刚修改的状态,业务依赖会随任务变化。
因此,不能仅凭工具名为Read,就认定它在任何位置都能提前执行。允许多个读取共同工作,与允许一次读取越过前面的写入,需要分别作出决定。
执行器里的信号量也不承担这项判断。并发名额决定当前还能容纳多少任务,前面有没有一个未启动的写入则属于顺序问题。即使名额充足,依赖写后状态的读取也应该等待;即使把并发数调小,也不能据此保证等待队列中的写入先获得启动机会。


