【问题标题】:F# update list in multiple threads at same timeF#同时在多个线程中更新列表
【发布时间】:2017-07-28 07:46:49
【问题描述】:

我是 F# 的新手,所以也许解决方案对某人来说很清楚,但我找不到。
想象一个包含世界区块的游戏世界(类似于 Minecraft),但面向更多玩家。
理论上,像 C++、java 或 C# 这样的语言可以同时修改多个世界块。两个或多个玩家尝试在不同的区块中放置或移除方块,所有这些动作都可以改变世界的状态而不会相互影响,只要每个区块中发生的动作不超过一个。序列化只会在一个块中的多个玩家执行修改时发生。

我对 F# 的理解是我需要在全局级别序列化这些动作,并且在整个世界中不可能同时发生两个动作,因为更新函数需要 actual world state update params(like add/remove blok) 并返回 new world state
例如,world state 包含 chunk list

有没有办法同时进行世界更新?
world state 能否以不同的方式存储以允许同时更新多个块?

【问题讨论】:

  • 在大多数与游戏相关的代码中,我会使用array 而不是list,除非您特别需要list 语义(从前面不可变地添加和删除项目)。如果您在固定大小上进行大量映射,数组通常会更快。
  • arraylist 与我试图描述的问题无关。如果我正确理解 F#,我无法修改 arraylist 元素,我需要使用已更改的元素创建一个新的 arraylist,这不会发生在多个线程上,因为只有一个新的 @987654336 @ 或 lists 将适用。
  • 其实数组是可变的,所以你可以修改元素,但我不建议你这样做。我只是说,既然你说的是游戏,那么数组是一个很好的默认性能。

标签: multithreading f#


【解决方案1】:

听起来您需要确保每个块一次运行一个操作。您可以通过将状态片段存储在邮箱处理器(通常称为“代理”)中来保护它们。您可以从多个线程向代理发送多条消息。它们将被排队并一次处理一个。

这里有详细的讨论:https://fsharpforfunandprofit.com/posts/concurrency-actor-model/

【讨论】:

  • 这就是我的想法,但在world object。您是否建议将MailboxProcessor list 存储在world state 而不是chunk list 中?每个MailboxProcessor 将代表chunk agent 更新chunk 并且在运行时不需要list 更新以反映世界编辑操作。我不确定,我需要编写该代码来确认它,但看起来它可以像魅力一样工作:)
  • @Jakub 没错,它可以是代理列表,或者包含代理或可以发布到代理的函数的列表。
【解决方案2】:

首先,我并没有向a previous answer 添加任何技术细节,所以如果您喜欢他们的解决方案,您应该继续并将其标记为答案。但是,我希望这能提供一些额外的背景信息......

您的问题的根本问题是,您需要世界的状态有多一致才能做出有关修改块的决定。

考虑一个我有两个块的世界,我们称它们为 A 和 B。考虑我想从块 A 添加或删除一个块的用例。所有重要的问题是:

  • 我是否需要知道块 B 中的块才能验证,然后从块 A 中添加/删除块。

例如,如果我的世界中只有有限数量的块,我可能需要这些信息来验证我是否可以在不超出限制的情况下实际添加块。这里的关键是我的“一致性边界”是我的整个世界——为了向块 A 添加一个新块,我需要关于我的世界中所有事物的一致信息。如果在我做出决定的中途另一个线程跳入并向块 B 添加一个块是不好的。如果这是一个要求,那么你别无选择 - 即使在 C#/C++ 情况下 - 你需要锁定对您的世界的访问,因此任何时候都只能执行一项此类操作。

从您提出问题的方式来看,我怀疑情况并非如此。在这种情况下,我们需要准确检查您的一致性要求是什么。一个较弱的要求是,如果我将块添加到块 A,我至少必须有关于块 A 中块的数量(和位置)的一致信息。在 C#/C++ 的情况下,这意味着必须在周围放置锁访问单个“块数据”,而不是整个世界。

在 F# 中对此进行建模的一种简单方法是(使用 this answer 中的建议):

open FSharp.Core

type ChunkMessage = 
    AddBlock
    | RemoveBlock

type MyWorld = 
    {
        Blocks : List<MailboxProcessor<ChunkMessage>>
    }

注意MyWorld 是一个可变的,但每个MailboxProcessor 封装的状态只能通过一次处理一条消息来改变。

Blocks 的实现不必是MailboxProcessor 的列表,您可以使用线程安全的对象集合,这些对象具有线程安全方法,但在这里使用它们正如The Quick Brown Fox 所建议的那样,导致了一个特别好的编程模型。

【讨论】:

    猜你喜欢
    • 2021-04-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-01-29
    • 2017-08-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多