并发与放置¶
System 的图规定了依赖;互不依赖的部分可以同时推进。roplat 0.5 怎样把它们放到 Tokio 任务和线程上,规则很少,而且与语句的书写顺序无关。
三种放置¶
| 放置 | 什么时候 | 对象的要求 |
|---|---|---|
| 同任务 join | 默认。节律周期内、match 分支内的并发一律如此 | 可以借用互不重叠的对象 |
tokio::spawn |
只在根作用域:一次 System 运行中只执行一次的部分里,互不相连的子图 | 被移入的对象须拥有、Send + 'static |
| hard 节律的线程 | rhythm.hard(),由拥有型域(move 闭包形式的周期域)进入 |
节点和资源以本体移入、随结果交回,须 Send + 'static |
为什么周期里不 spawn。 节律周期和 match 分支会反复执行,每次 spawn 都要分配一个任务、花几微秒。同任务 join 没有这些开销,还能借用对象而不必转移所有权。
为什么 spawn 不能隔离重计算。 任务里的同步代码一样会占住它所在的工作线程。需要隔离的是规划、推理、长时间的外语调用这类同步重计算,办法是 hard 节律:每次 drive 在它自己的线程和运行时上执行,调用方等待时不占线程,同任务的兄弟照常推进。见 hard 节律。
所有权需求向外传播¶
同任务 join 可以借用对象;真正 spawn 或进入拥有型域的部分需要对象本体。这个需求由宏逐层向外传播,直到创建对象的那一层。某一层只有引用(例如对象是 &mut T 参数)时,Rust 会报出类型或生命周期错误。
所以一段只在串行时能编译的代码,加入独立分支或拥有型域之后可能需要调整:让创建层以本体持有对象,或让它满足 Send + 'static。
调度与书写顺序无关¶
编译器按依赖和名字为图编号,同一张图换一种语句顺序得到相同的调度。资源顺序和被其他路径蕴含的数据边不会改变图的串并结构,只有图本身需要切开时才作为边加回。图中的环和会造成通道死锁的结构给出带位置的编译诊断。
可以用 cargo roplat graph 画出图和规约后的调度树,见查看静态图。
不保证什么¶
- 静态图不等于硬实时:Tokio 定时器、操作系统调度、节点里的阻塞调用和外语运行时都会影响时序。
- 编译期分析覆盖支持的图形状,不覆盖节点内部的锁、外部协议的等待或任意用户代码,不能称为"消除了所有死锁"。
- hard 节律隔离线程,但 0.5 还没有实时优先级和绑核参数。
- 一个 System 里节点很多时编译会变慢:约 150 个节点起 rustc 出现递归深度警告,约 200 个节点需要在 crate 根加
#![recursion_limit = "256"]。