小编典典

C# 对象池模式实现

all

有没有人有很好的资源来为 Sql 连接池的有限资源实施共享对象池策略?(即完全实现它是线程安全的)。

为了跟进@Aaronaught
澄清请求,池的使用将用于对外部服务的负载平衡请求。把它放在一个可能比我直接的情况更容易立即理解的场景中。我有一个会话对象,其功能类似于ISessionNHibernate
中的对象。每个唯一会话都管理它与数据库的连接。目前我有 1 个长时间运行的会话对象,并且遇到了我的服务提供商限制我对这个单独会话的使用的问题。

由于他们不期望单个会话将被视为长期运行的服务帐户,因此他们显然将其视为正在打击其服务的客户。这让我想到了这里的问题,我将创建一个不同会话池,并将请求拆分到多个会话中的服务,而不是像以前那样创建一个焦点,而不是创建一个单独的会话。

希望背景提供一些价值,但直接回答您的一些问题:

问: 创建对象是否昂贵?
答: 没有对象是有限资源池

问: 它们会被非常频繁地收购/发布吗?
A: 是的,他们可以再次被认为是 NHibernate ISessions,其中 1 通常在每个单个页面请求的持续时间内被获取和释放。

问: 简单的先到先得就足够了,还是您需要更智能的东西,即可以防止饥饿?
答:
一个简单的循环类型分发就足够了,我假设你的意思是如果没有可用的会话,调用者会被阻塞等待发布。这实际上并不适用,因为会话可以由不同的呼叫者共享。我的目标是在多个会话中分配使用,而不是
1 个会话。

我相信这可能与对象池的正常使用有所不同,这就是为什么我最初忽略了这部分并计划只是调整模式以允许共享对象,而不是让饥饿情况发生。

问: 优先级、延迟加载与急切加载等问题呢?
答: 不涉及优先级,为简单起见,假设我将在创建池本身时创建可用对象池。


阅读 56

收藏
2022-08-21

共1个答案

小编典典

.NET Core 中的对象池

dotnet 核心在基类库 (BCL)
中添加了对象池的实现。您可以在此处阅读原始 GitHub
问题并查看System.Buffers的代码。目前ArrayPool是唯一可用的类型,用于池化数组。这里有一篇不错的博文。

namespace System.Buffers
{
    public abstract class ArrayPool<T>
    {
        public static ArrayPool<T> Shared { get; internal set; }

        public static ArrayPool<T> Create(int maxBufferSize = <number>, int numberOfBuffers = <number>);

        public T[] Rent(int size);

        public T[] Enlarge(T[] buffer, int newSize, bool clearBuffer = false);

        public void Return(T[] buffer, bool clearBuffer = false);
    }
}

在 ASP.NET Core 中可以看到其使用示例。因为它在 dotnet core BCL 中,所以 ASP.NET Core
可以与其他对象共享它的对象池,例如 Newtonsoft.Json 的 JSON
序列化程序。您可以阅读这篇博文,了解有关 Newtonsoft.Json
如何做到这一点的更多信息。

Microsoft Roslyn C# 编译器中的对象池

新的 Microsoft Roslyn C#
编译器包含ObjectPool类型,该类型用于汇集经常使用的对象,这些对象通常会被更新并经常被垃圾收集。这减少了必须发生的垃圾收集操作的数量和大小。有几个不同的子实现都使用了
ObjectPool(请参阅:为什么在 Roslyn
中有这么多对象池的实现?
)。

1 -
SharedPools
- 如果使用 BigDefault,则存储 20 个对象或 100 个对象的池。

// Example 1 - In a using statement, so the object gets freed at the end.
using (PooledObject<Foo> pooledObject = SharedPools.Default<List<Foo>>().GetPooledObject())
{
    // Do something with pooledObject.Object
}

// Example 2 - No using statement so you need to be sure no exceptions are not thrown.
List<Foo> list = SharedPools.Default<List<Foo>>().AllocateAndClear();
// Do something with list
SharedPools.Default<List<Foo>>().Free(list);

// Example 3 - I have also seen this variation of the above pattern, which ends up the same as Example 1, except Example 1 seems to create a new instance of the IDisposable [PooledObject<T>][4] object. This is probably the preferred option if you want fewer GC's.
List<Foo> list = SharedPools.Default<List<Foo>>().AllocateAndClear();
try
{
    // Do something with list
}
finally
{
    SharedPools.Default<List<Foo>>().Free(list);
}

2 -
ListPoolStringBuilderPool
- 不是严格分开的实现,而是围绕上面所示的 SharedPools 实现的包装器,专门用于 List 和 StringBuilder。所以这会重用存储在
SharedPools 中的对象池。

// Example 1 - No using statement so you need to be sure no exceptions are thrown.
StringBuilder stringBuilder= StringBuilderPool.Allocate();
// Do something with stringBuilder
StringBuilderPool.Free(stringBuilder);

// Example 2 - Safer version of Example 1.
StringBuilder stringBuilder= StringBuilderPool.Allocate();
try
{
    // Do something with stringBuilder
}
finally
{
    StringBuilderPool.Free(stringBuilder);
}

3 -
PooledDictionaryPooledHashSet
- 它们直接使用 ObjectPool 并具有完全独立的对象池。存储 128 个对象的池。

// Example 1
PooledHashSet<Foo> hashSet = PooledHashSet<Foo>.GetInstance()
// Do something with hashSet.
hashSet.Free();

// Example 2 - Safer version of Example 1.
PooledHashSet<Foo> hashSet = PooledHashSet<Foo>.GetInstance()
try
{
    // Do something with hashSet.
}
finally
{
    hashSet.Free();
}

Microsoft.IO.RecyclableMemoryStream

该库为MemoryStream对象提供池化。它是System.IO.MemoryStream. 它具有完全相同的语义。它是由 Bing
工程师设计的。阅读此处的博文或查看GitHub
的代码。

var sourceBuffer = new byte[]{0,1,2,3,4,5,6,7}; 
var manager = new RecyclableMemoryStreamManager(); 
using (var stream = manager.GetStream()) 
{ 
    stream.Write(sourceBuffer, 0, sourceBuffer.Length); 
}

请注意,RecyclableMemoryStreamManager应该声明一次,它将在整个过程中存活——他就是池。如果您愿意,可以使用多个池。

2022-08-21