我的金融软件不断处理几乎相同的对象。例如,我在线有这样的数据:
HP 100 1
HP 100 2
HP 100.1 1
etc.
我每秒钟大约有1000次更新。
每个更新都存储在对象中 - 但我不想动态分配这些对象以改善延迟。 我只在很短的时间内使用对象 - 我收听它们,申请和免费。一旦对象空闲,它实际上可以重用于另一个数据包。
所以我需要一些存储(可能是环形缓冲区)来分配所需数量的对象一次,它们允许“获取”和“释放”它们。在c#中最好的方法是什么?
每个对象都有id
,我按顺序分配id's
并释放它们sequentially
。
例如,我收到了ID 1
2
和3
,然后我免费1
,2
,3
。因此任何FIFO集合都可以工作,但我正在寻找一些涵盖所需功能的库类。
即。我需要FIFO集合,它不分配对象,但重用它们并允许重新配置它们。
UPD
我添加了我想要的实现。这不是经过测试的代码,可能有错误。
想法很简单。作者应该调用Obtain
Commit
方法。读者应该调用TryGet
方法。读者和编写者可以从不同的线程访问这个结构:
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++;
}
}
欢迎关于我的实现的评论,可能一些库方法可以取代这个简单的类?
答案 0 :(得分: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
}
}
答案 1 :(得分:3)
如果负载突然出现,您可以通过延迟收集来使用GC's latency modes来抵消开销。这不是一个银弹,但在某些情况下它可能非常有用。
答案 2 :(得分:0)
我不确定,如果这是您需要的,但您总是可以创建一个将要使用的对象池。初始化对象类型的列表。然后,当您需要使用一个对象时,将其从列表中删除,并在完成后将其添加回来。
http://www.codeproject.com/Articles/20848/C-Object-Pooling是一个好的开始。
希望我有所帮助,即使有点:)
答案 3 :(得分:-2)
如果您只是担心GC运行所花费的时间,那么请不要 - 它不能被您自己做的任何事情打败。
但是,如果你的对象的构造函数做了一些工作,可能会更快地缓存它们。
一种相当直接的方法是使用ConcurrentBag
基本上你要做的是使用ConcurrentBag.Add()
用一组对象预先填充它(如果你想要的话 - 或者你可以从空开始并让它成长)。
然后,当您需要一个新对象时,使用ConcurrentBag.TryTake()
来抓取一个对象。
如果TryTake()
失败,那么您只需创建一个新对象并使用它。
无论您是从包中抓取物品还是创建了一个新物品,一旦完成它,您只需使用ConcurrentBag.Add()
将该物品放回包中
一般来说,你的包将达到一定的尺寸,但不会更大(但你可能想要检查一下)。
在任何情况下,我总会做一些时间来看看这个实际之类的变化是否会产生任何影响。除非对象构造函数正在做相当多的工作,否则它可能不会产生太大的影响。