【问题标题】:Sharing only some fields of a struct between threads在线程之间仅共享结构的某些字段
【发布时间】:2021-06-02 17:35:17
【问题描述】:

我正在编写一个使用两个线程的模拟程序:一个用于实际模拟,另一个用于渲染。

模拟线程具有Entity 结构中的Vec,它使用每个tick。当渲染线程需要访问实体的Vec 以渲染它们时,就会出现我的问题,但是Entity 结构的某些字段没有实现Send 特征。但是,这些字段实际上并不用于渲染。

我想到的唯一解决方案是将每个实体拆分为两个结构:一个包含内部“不可发送”字段,另一个包含渲染所需的字段。然后我可以使用两个单独的Vecs 并将“可发送”的一个包装在Mutex 中并与渲染线程共享。然而,这意味着每个实体将有两个部分,并且每个结构都必须存储另一部分的索引以将它们联系在一起,但是当实体被创建和删除很多时,管理起来感觉非常头疼。

在 Rust 中有标准的方法吗?对于很多程序来说,这感觉是一个相当普遍的要求,但我找不到一个简单的方法来做到这一点。

【问题讨论】:

  • 不实现Send 的Rust 对象实际上非常罕见——只有RcRefCell 会浮现在脑海中,原始指针也是如此。您的字段有哪些非发送类型?
  • 如果您的字段必须保持非Send,一种不涉及拆分实体的可能解决方案是使用(命名不当)fragile crate。然后您可以将非Send 字段包装在Fragile<T> 中,这将阻止它们在原始线程之外使用(使用运行时检查),但允许将整个对象传递给其他线程,并允许您可以使用实际上是 Send 的字段。
  • Send 类型来自 pyo3 crate,所以它们是不可避免的 - 我会研究一下脆弱的 crate,它看起来很有希望
  • 希望对您有所帮助,祝您好运。出于好奇,哪些 pyo3 类型是非发送的?通过查看 pyo3 文档,我了解到它围绕 Python 对象的包装器是 SendSync,但使用它们需要提供 Python GIL 令牌作为获得 GIL 的证明。例如。 Py<T>SendSync
  • 我使用的类型 PyAny 没有实现 SendSync。我现在已经将这个值包装在Py 类型中,留下一个Py<PyAny>,它实际上可以在线程之间发送!我仍然会在答案中使用 ECS 设计,因为它会使我的程序更高效

标签: multithreading rust


【解决方案1】:

您在 split-Entity 设计上走在了正确的轨道上,但您可以通过 Entity-Component-System (ECS) 模式的提示更进一步。 ECS 模式说 entity 只是 组件。在一个简单的模型中,这将类似于

struct Sim { /* ... */ }
struct Render { /* ... */ }

struct Entity {
    sim_id: usize,
    render_id: usize,
}

struct World {
    entities: Vec<Entity>,
    sims: Vec<Sim>,
    renders: Arc<Mutex<Vec<Render>>>,
}

那么,在 ECS 术语中,系统 只是一个作用于组件而不是实体的函数。这种模式可以很好地并行化,因为给定的系统只能锁定它需要的那些组件,而不是锁定所有实体数据。这是一个playground,它带有一个简单(有点幼稚)的概念验证,带有一个与模拟线程共享数据的单独渲染线程。

如果您正在寻找实现或灵感,Rust 有 numerous ECS libraries。您可以在 rust-gamedev/ecs-bench-suite 中找到各种选项的广泛基准测试。

【讨论】:

  • 我认为这个系统不支持从列表中删除实体?
猜你喜欢
  • 2018-07-30
  • 1970-01-01
  • 2010-12-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多