tokio 的 RwLock 保护的多个字段的序列化

Serialization of multiple fields protected by tokio's RwLock

我使用 tokio 来监听 UDP 套接字。我 send/receive bincode 编码结构和 serialize/deserialize 它们 serde.

但是我应该如何处理 Arc<tokio::sync::RwLock<T>> 字段的序列化? (我知道 serde 支持标准库同步 RwLock 但我需要异步)。

数据经常被更改并通过网络传输(按设计),因此每次我需要发送内容时等待多个读锁可能不是一个好主意。

考虑到 serde 是同步的,我必须为每个 RwLock 字段生成阻塞 tokio 任务。 (每个字段都有自己的 RwLock 个字段)。所以这会变得非常慢。

rust 处理这个问题的方法是什么?

示例:

use serde::{Deserialize, Serialize};
use tokio::sync::RwLock;

#[derive(Serialize, Deserialize)]
struct Player {
  // In order to serialize Player i'd have to one by one acquire lock for `meta` and then `stats`, and then some other potential fields.
  pub id: u64,
  pub meta: Arc<RwLock<Metadata>>,
  pub stats: Arc<RwLock<Metadata>>,  
  pub someOtherField1: Arc<RwLock<...>>,
  pub someOtherField2: Arc<RwLock<...>>,
  pub someOtherField3: Arc<RwLock<...>>,
}

Data is being changed and transferred over the network fairly often (by design), so it's probably not a good idea to wait for multiple read locks every time i need to send something.

忘记性能 - 这不会成为您在任何类型的游戏网络中的瓶颈(这确实出现了您正在尝试做的事情)。

唯一正确的方法是在序列化数据之前对每个字段获取读锁,然后将它们全部序列化。如果你不这样做,你最终会得到不一致的状态 ,在逻辑上不存在 。玩家可能还活着但生命值为 0,或者在游戏中已断开连接。

这可能会导致死锁!这是您开始共享数据时选择的难度。有一个非常好的经验法则可以防止死锁,如果你虔诚地坚持它,你必须在你的代码中做到这一点:任何获取多个锁的例程都必须以相同的顺序获取它们。 如果需要一个新锁,但我们已经有其他锁在顺序后面,我们必须先释放所有锁,然后再以正确的顺序再次获取它们。

坚持上述做法的最简单方法是只为玩家设置一把锁。