虽然.NET框架提供一些池的对象,但在应用时有时也不想引用其他库时就需要自己编写一些简单的对象池。由于在编写对象池的时候需要考虑到线程安全,一般在设计时使用非线程安全结构和加锁来处理或使用System.Collections.Concurrent来解决线程安全的问题。显然 System.Collections.Concurrent更多使用更轻量的自旋锁来处理并发问题,看上去似乎比传统的Lock方式有着更好的性能优势!
不过最近在优化BeetleX的新版时让我发现另一种 Lock的使用方式似乎有着更好的处理效果。在翻阅ArrayPool<T>的代码时候发现内部实现就是简单粗暴的方式LOCK+数据组来实现一个高效的线程安全Stack结构。而里面完全没有使用.Net自带的Stack和ConcurrentStack结构。虽然代码使用了Lock处理,但代码使用了一些特别设计来解决多线程并发时由Lock引发的性能问题。实现过程中 ArrayPool<T>还进行了内部存储分区,然后通过Thread.GetCurrentProcessorId()来确定操作区降低多线程并发时Lock带来的性能问题。
通过以上思想Beetlex内部的对象分别使用了分区Stack+ Lock和
ConcurrentStack的设计,发现两者测试下来的性能基本一致,但程序内存占有则有着巨大的差异!由于
ConcurrentStack内部有相应的结构产生在高并发时候会生产非常多的内存开销和回收。而使用
分
区
Stac
k侧内存开销非常平稳!
以下是测试结果情况
分 区 Stac k
internal classPartition
{
private Stack<MemoryBlock> mData = new Stack<MemoryBlock>();
publiclong Length => mData.Count;
public MemoryBlock Pop()
{
lock (this)
{
if (mData.Count > 0)
return mData.Pop();
returnnull;
}
}
publicvoidPush(MemoryBlock data)
{
lock (this)
{
mData.Push(data);
}
}
}
分区ConcurrentStack
internal classPartition
{
private System.Collections.Concurrent.ConcurrentStack<MemoryBlock> mData = new System.Collections.Concurrent.ConcurrentStack<MemoryBlock>();
publiclong Length => mData.Count;
public MemoryBlock Pop()
{
mData.TryPop(outvar result);
return result;
}
publicvoidPush(MemoryBlock data)
{
mData.Push(data);
}
}
BeetleX
开源跨平台通讯框架(支持TLS)
提供HTTP,Websocket,MQTT,Redis,RPC和服务网关开源组件
个人微信:henryfan128 QQ:28304340
关注公众号
https://github.com/beetlex-io/