1

我的财务软件不断地处理几乎相同的对象。例如我在网上有这样的数据:

HP 100 1
HP 100 2
HP 100.1 1
etc.

我每秒大约有 1000 次更新。

每个更新都存储在对象中 - 但我不想动态分配这些对象以改善延迟。我只在短时间内使用对象 - 我收到它们,申请和免费。一旦对象是空闲的,它实际上可以被重新用于另一包数据。

所以我需要一些存储(可能是环形缓冲区)来分配所需数量的对象一次,它们允许“获取”和“释放”它们。在 c# 中做到这一点的最佳方法是什么?

每个对象都有,我也按顺序id分配并释放它们。例如,我收到 id和,然后我免费, , 。所以任何 FIFO 集合都可以工作,但我正在寻找一些涵盖所需功能的库类。id'ssequentially1 23123

即我需要不分配对象但重用它们并允许重新配置它们的 FIFO 集合。

更新

我已经添加了我想要的实现。这不是经过测试的代码,可能有错误。想法很简单。作家应该调用Obtain Commit方法。读者应该调用TryGet方法。Reader 和 writer 可以从不同的线程访问这个结构:

public sealed class ArrayPool<T> where T : class
{
    readonly T[] array;
    private readonly uint MASK;

    private volatile uint curWriteNum;
    private volatile uint curReadNum;

    public ArrayPool(uint length = 1024) // length must be power of 2
    {
        if (length <= 0) throw new ArgumentOutOfRangeException("length");
        array = new T[length];
        MASK = length - 1;
    }

    /// <summary>
    /// TryGet() itself is not thread safe and should be called from one thread.
    /// However TryGet() and Obtain/Commit can be called from different threads
    /// </summary>
    /// <returns></returns>
    public T TryGet()
    {
        if (curReadNum == curWriteNum)
        {
            return null;
        }
        T result = array[curReadNum & MASK];
        curReadNum++;
        return result;
    }

    public T Obtain()
    {
        return array[curWriteNum & MASK];
    }

    public void Commit()
    {
        curWriteNum++;
    }

}

欢迎对我的实现发表评论,并且可能一些库方法可以替换这个简单的类?

4

4 回答 4

5

根据我对这个问题的评论,我认为你不应该跳过这一点 - 但是,一个简单的方法是这样的:

public sealed class MicroPool<T> where T : class
{
    readonly T[] array;
    public MicroPool(int length = 10)
    {
        if (length <= 0) throw new ArgumentOutOfRangeException("length");
        array = new T[length];
    }
    public T TryGet()
    {
        T item;
        for (int i = 0; i < array.Length; i++)
        {
            if ((item = Interlocked.Exchange(ref array[i], null)) != null)
                return item;
        }
        return null;
    }
    public void Recycle(T item)
    {
        if(item == null) return;
        for (int i = 0; i < array.Length; i++)
        {
            if (Interlocked.CompareExchange(ref array[i], item, null) == null)
                return;
        }
        using (item as IDisposable) { } // cleaup if needed
    }
}
于 2013-05-17T12:51:33.130 回答
3

如果负载突发,您可以使用GC 的延迟模式通过延迟收集来抵消开销。这不是灵丹妙药,但在某些情况下它会很有帮助。

于 2013-05-17T15:30:46.537 回答
0

我不确定,如果这是你需要的,但你总是可以创建一个将要使用的对象池。初始化对象类型的列表。然后,当您需要使用某个对象时,将其从列表中删除,并在完成后将其添加回来。

http://www.codeproject.com/Articles/20848/C-Object-Pooling是一个好的开始。

希望我有所帮助,即使有点:)

于 2013-05-17T12:34:13.737 回答
-2

如果您只是担心 GC 运行所花费的时间,那么不要担心 - 您可以自己做的任何事情都无法打败它。

但是,如果您的对象的构造函数做了一些工作,那么缓存它们可能会更快。

一个相当简单的方法是使用ConcurrentBag

本质上,您所做的是使用一组对象预先填充它ConcurrentBag.Add()(也就是说,如果您愿意 - 或者您可以从它空开始并让它增长)。

然后,当您需要一个新对象时,您可以ConcurrentBag.TryTake()用来抓取一个对象。

如果TryTake()失败,那么您只需创建一个新对象并使用它。

无论您是从包中抓起一个对象还是创建一个新对象,一旦完成,您只需使用该对象将该对象放回包中ConcurrentBag.Add()

一般来说,你的包会达到一定的尺寸,但不会变大(但你可能想用仪器检查一下)。

无论如何,我总是会做一些时间来看看这样的改变是否真的有什么不同。除非对象构造函数做了相当多的工作,否则它可能不会有太大的不同。

于 2013-05-17T12:29:34.457 回答