- 原文:[https://without.boats/blog/the-scoped-task-trilemma/](https://without.boats/blog/the-scoped-task-trilemma/)
- 背景:
- 移植 scoped thread API 到 [scoped tasks](https://tmandry.gitlab.io/blog/posts/2023-03-01-scoped-tasks/)
- 发现 API 的几个期望特性最终彼此之间是不兼容的。
- 原因在于 Rust 现有类型系统的局限性。
- scoped task trilemma,但不局限于 scoped tasks
- **Concurrency**: child tasks 和 **parent** 可以并发进行
- **Parallelizability**:child tasks 和 **parent** 可以并行执行
- **Borrowing**:child tasks 可以从 parent 中 borrow data,而不需要同步(synchronization)。
- 三个特性无法同时满足,导致 scoped task API 不是 sound 的、io-uring等特性无法实现。
- 每个 sound 的 concurrency API 最终只提供了三个特性中的两个。
----
# Concurrency + Parallelizability
- 代表例子:
- **`thread::spawn`**
- **`task::spawn`**
- 这些 APIs 允许 child task 与 parent task 独立且并发执行。
- child task 可以在另外一个 thread 上执行,可能是 in parallel,同时不会打断 parent task 的执行。
- 另一方面,child task 无法从 parent task 进行 borrow,这些 APIs 的参数都必须是 `'static` 的。
- 这些 APIs 允许 join child task 来等待它完成,但不是必须要这么做。
- 像异步的 `task::spawn` API ,当等待时,你可以在相同线程上运行当前作用域之外的 task。
- 即 executor 的线程,运行其他代码 spwan 的 task
------
# Parallelizability + Borrowing
- 代表例子:
- **`thread::scope`**
- **`rayon::join`**
- 这些 APIs 允许 child task 跟 parent 独立处理,同时从 parent 进行 borrowing。
- child task 可以在另外的线程执行,同时从 parent context 中 borrowing。
- 限制:child scope 和 parent scope 之间的代码无法并发运行(concurrently)。
- 这些 APIs 强制要求你在返回控制权给 caller 之前必须 join child task,这是他们确保跨线程 borrowing 符合 lifetime signature 的方式。
---
# Borrowing + Concurrency
- 代表例子:
- **`select!`**
- **`FuturesUnordered`**
- 这些 APIs 可以实现 child tasks 跟 parent task 并发(concurrently)处理,同时可以从 parenet borrowing。
- 限制:child tasks 不能独立于 parent task 运行,这些 APIs 没有 joining 的概念。
- 被 selected 或 streamed 的 futures 作为与 parent 相同 work unit 的一部分运行。
- 并且在执行期间不能 move 到另一个线程,除非与 parent 作为一个单独的单元一起 move。
----
# Why not all three?
- scoped thread 和 rayon APIs 可以同时支持 parallelizable 和 borrowing,而无需同步。实际上是需要同步的:
- 同步发生在这些 APIs join 子线程时,返回 caller 之前。
- 然而,这种同步被摊销为函数结束时的单个等待操作,而不需要某种同步来共享或传递所有状态给不同的任务。
- 旧版的 scoped thread API 组织上是不同的,它是当 join handle 被 drop 时才进行 join。
- 旧版的 API 是 unsound 因为 Rust 中的对象可以 Leak 出去,而不需要调用析构函数。
- [Leakpocalypse](https://cglab.ca/~abeinges/blah/everyone-poops/#leakpocalypse)问题
- 也就是为什么最近在讨论支持不能被 Leak 的类型。最大问题是会破坏向后兼容。
- 在新的 scoped thread API 中,interior scope(可以与 parallelized tasks 并发执行)是由传递给 `scope` 函数的闭包在词法上定义的, exterior scope 是在此闭包之外的任何东西。
- 在老的 scoped thread API 中,interior scope 的定义更隐式,通过 child thread join handle 存活的代码段。
---
# Why not just two?