我有一个代表“世界状态”的类。该类具有许多其他对象的许多集合,这些对象反过来又引用更多对象和对象集合,有时甚至引用它们在“世界层次结构”中的祖先。为了简化所说的内容,下面是一个示例(转换为 XML,详细信息省略):
<World>
<Country>
<City>
<Block>
...
</Block>
</City>
<City>
<Block>
...
</Block>
</City>
</Country>
...
<Country>
<City>
<Block> ... </Block>
...
<Block> ... </Block>
</City>
<City>
<Block> ... </Block>
</City>
</Country>
</World>
有两个线程,UI线程和后台线程(实际上是服务器)。
服务器接收修改“世界状态”的消息(添加城市、街区等)。
UI 线程每隔一段时间就会使用 PictureBox 对象将世界状态绘制到屏幕上。表示层仅引用 IWorld 对象(由 World 实现),并且无法访问其中的元素。
UI 线程应锁定世界的完整状态,以便在绘制过程中(服务器)无法更改世界(这会导致世界图片不一致)。由于它仅引用 IWorld 对象,因此这是唯一要锁定的对象。
我的问题是这个锁是否足够(即递归地锁定该对象拥有的所有字段和属性),或者每个对象应该单独锁定。解决这个问题的正确方法是什么?
注意: UI 无法联系服务器(这意味着它无法告诉服务器停止改变世界,然后告诉它在渲染后恢复)。
编辑: 如果 World 和层次结构中的所有类都实现 ILock 接口(interface),该接口(interface)提供 Lock() 方法,该方法将在所有较低级别上(递归地)调用 Lock(),这可能容易出现死锁(循环引用)或成本太高。
我认为设计变更是适当的。
最佳答案
您不需要递归锁定 - 如果两个线程仅锁定同一个 IWorld
引用,则足以确保相互排他性。
锁定各个元素当然会给您提供更细粒度的锁定,但最终可能会得到不一致的图片 - 我想象您希望看到一整套更改一次从服务器。
但是,更好的模型通常是让服务器发布“快照”,然后创建世界的新副本并修改该副本,在适当的时候将其转换为快照。这样,您就不需要这种互斥,这种互斥可能会在服务器更改世界时阻止您的 UI 绘制世界。
关于c# - .NET 中对象的锁定层次结构,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1592818/