首页 > 解决方案 > 如何正确确定循环池中的活动实例?

问题描述

我有一个循环池实现

public class CircularPool<T> where T : class, new()
{
    private readonly Queue<T> instances;
    
    public CircularPool(int size)
    {
        instances = new Queue<T>(size);
        
        for (var i = 0; i < size; ++i)
        {
            instances.Enqueue(new T());
        }
    }

    public T GetInstance()
    {
        var instance = instances.Dequeue();
        instances.Enqueue(instance); // Circular pool
        return instance;
    }

    public void ReturnInstance(T instance)
    {
        // What does returning look like?
        // Could use IsActive flag?
    }

    public IEnumerable<T> GetActiveInstances()
    {
        return instances;  // TODO: Only want the active ones
    }
}

我希望能够跟踪池中当前正在使用的实例,以便可以迭代它们并执行各种操作

var pool = new CircularPool<Foo>(5);
var fooInstanceA = pool.GetInstance();
var fooInstanceB = pool.GetInstance();
var fooInstanceC = pool.GetInstance();

pool.ReturnInstance(fooInstanceB);

foreach (var activeInstance in pool.GetActiveInstances())
{
    // Do stuff with active instances (fooInstanceA then fooInstanceC)
}

如何按年龄顺序正确获取活动实例?

鉴于这是一个循环池,那么如果调用 GetInstance 的次数超过了池的大小而没有调用 ReturnInstance,则返回最旧的活动实例并成为最年轻的活动实例。这应该相应地反映在 GetActiveInstances 中。

此外,在循环池的上下文中 ReturnInstance 应该是什么样子?

标签: c#queue

解决方案


好吧,我们可以T[]所有实例HashSet<T>活动实例使用数组:

public class CircularPool<T> where T : class, new() {
  private readonly T[] m_All;
  private readonly HashSet<T> m_InUse;

  private int m_Index; // Index to start looking for a free instance

  public CircularPool(int size) {
    if (size <= 0)
      throw new ArgumentOutOfRangeException(nameof(size));

    m_InUse = new HashSet<T>(size);
    m_All = Enumerable.Range(0, size).Select(_ => new T()).ToArray();
  }

  public bool TryGetInstance(out T availableInstance) {
    for (int i = 0; i < m_All.Length; ++i) {
      int index = (i + m_Index) % m_All.Length;

      if (m_InUse.Add(m_All[index])) {
        availableInstance = m_All[index];
        m_Index = index + 1;

        return true;
      }
    }

    availableInstance = default(T); // no available instances found
    return false;
  }

  public T GetInstance() => TryGetInstance(out var result)
    ? result
    : throw new InvalidOperationException("There are no available instances.");

  public bool ReturnInstance(T instance) => m_InUse.Remove(instance);

  public IEnumerable<T> GetActiveInstances() => m_InUse;

  public IEnumerable<T> GetAvailableInstances() => 
    m_All.Where(item => !m_All.Contains(item));
}

推荐阅读