管理生成对象池

Managing Pool of Generated Objects

我正在开发一个项目,其中地图的各个区域要么动态生成,要么从文件加载(如果已经生成并保存)。区域仅 loaded/generated 根据需要,并在不再需要时保存和丢弃。 有几种不同的任务将出于各种目的使用此地图的一个或多个区域。例如,这些任务之一是绘制所有当前可见的区域(在任何给定时间大约 9 个)。另一种是获取有关区域的信息,甚至修改区域。 问题是这些任务可能会或可能不会与其他任务使用相同的区域。

由于这些区域相当大,而且生成成本很高,因此(出于这些和其他原因)为每个任务使用不同的副本会产生问题。 相反,我认为创建和管理当前加载区域的池是个好主意。新任务将首先检查其所需区域的池。然后他们可以使用它(如果存在),或者创建一个新的并将其添加到池中。

如果可行,我将如何管理此池?我如何确定某个区域是否不再被任何任务需要并且可以安全地丢弃?我是不是太傻了,太复杂了?

如果这对任何人都重要,我正在使用 c#。

编辑: 现在我更清醒了,它会像在每个区域为每个使用的地方增加一个计数器一样简单吗?然后在计数器达到 0 时丢弃它?

好的,我不知道你的应用程序是如何设计的,但我建议你看看this

您还可以使用 static 与其他任务共享您的变量,但是您可能希望使用块变量来防止您在其他进程正在使用该变量时写入或读取该变量。 (here)

Provided that works, how would I manage this pool? How would I determine if a region is no longer needed by any tasks and can be safely discarded?

一个简单的方法是使用弱引用:

public class RegionStore
{
  // I'm using int as the identifier for a region.
  // Obviously this must be some type that can serve as
  // an ID according to your application's logic.
  private Dictionary<int, WeakReference<Region>> _store = new Dictionary<int, WeakReference<Region>>();
  private const int TrimThreshold = 1000; // Profile to find good value here.
  private int _addCount = 0;
  public bool TryGetRegion(int id, out Region region)
  {
    WeakReference<Region> wr;
    if(!_store.TryGetValue(id, out wr))
      return false;
    if(wr.TryGetTarget(out region))
      return true;
    // Clean up space in dictionary.
    _store.Remove(id);
    return false;
  }
  public void AddRegion(int id, Region region)
  {
    if(++_addCount >= TrimThreshold)
      Trim();
    _store[id] = new WeakReference<Region>(region);
  }
  public void Remove(int id)
  {
    _store.Remove(id);
  }
  private void Trim()
  {
    // Remove dead keys.
    // Profile to test if this is really necessary.
    // If you were fully implementing this, rather than delegating to Dictionary,
    // you'd likely see if this helped prior to an internal resize.
    _addCount = 0;
    var keys = _store.Keys.ToList();
    Region region;
    foreach(int key in keys)
      if(!_store[key].TryGetTarget(out wr))
        _store.Remove(key);
  }
}

现在您有一个 Region 对象的存储区,但如果不存在对它们的其他引用,该存储区不会阻止它们被垃圾回收。

Certain task will be modifying regions. In this case I will likely raise an "update" flag in the region object, and from there update all other tasks using it.

请注意,这将是整个应用程序中一个明确的潜在错误来源。可变性使任何类型的缓存都变得复杂。如果您可以转向不可变模型,它可能会简化事情,但过时对象的使用会带来其自身的复杂性。