C#对象池模式实现 [英] C# Object Pooling Pattern implementation

查看:187
本文介绍了C#对象池模式实现的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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



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



由于他们缺乏一个单一的期望会话将被视为长期运行的服务帐户,他们显然将其视为正在殴打其服务的客户端。这让我在这里提出了我的问题,而不是有1个会话,我将创建一个不同会话的池,并将这些请求分解为跨多个会话的服务,而不是像以前那样创建一个单一的焦点。



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



问:创建对象是否昂贵?

A:没有对象是有限资源池



Q:他们是否会被频繁收购/发布?

A:是的,他们再次可以想到NHibernate会话,其中1通常被收购并发布每页请求的持续时间。



问:一个简单的先到先得的足够或需要更智能的东西,即会阻止饥饿?

A:一个简单的循环型dist如果没有可用的会话,呼叫者被阻塞等待发布,那么通过饥饿来说,分配就足够了。这不是真的适用,因为会话可以被不同的呼叫者共享。我的目标是在多个会话之间分配使用情况,而不是1个会话。



我相信这可能与正常使用对象池的分歧是为什么我原来这个部分出来,只是为了适应这种模式,以允许共享对象,而不是允许饥饿情况发生。



Q: strong>如优先事项,懒惰与渴望加载等等?

A:没有涉及到优先级,为简单起见,假设我将创建池可用对象在创建池本身时。

解决方案

.NET Core中的对象池



dotnet core 将对象池的实现添加到基类库( BCL)。您可以阅读原始的GitHub问题此处,并查看代码 System.Buffers 。目前, ArrayPool 是唯一可用的类型,用于池数组:

 命名空间System.Buffers 
{
public abstract class ArrayPool< T>
{
public static ArrayPool< T>共享{get;内部集}

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

public T [] Rent(int size);

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

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

在ASP中可以看到其用法的一个例子。 NET核心。因为它是在dotnet核心BCL,ASP.NET Core可以与其他对象(如Newtonsoft.Json的JSON序列化程序)共享它的对象池。你可以阅读这个博客文章,了解更多有关Newtonsoft.Json如何执行此操作的信息。



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



新的Microsoft Roslyn C#编译器包含 ObjectPool 类型,用于汇集常用的对象,这些对象通常会被新建和垃圾回收。这减少了必须发生的垃圾收集操作的数量和大小。有几个不同的子实现都使用ObjectPool(参见:)。



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

  //示例1  - 在一个using语句中,所以对象在最后释放。 
使用(PooledObject< Foo> pooledObject = SharedPools.Default< List&Foo>>()GetPooledObject())
{
//使用pooledObject.Object执行某些操作
}

//示例2 - 没有使用语句,所以你需要确保没有抛出异常。
列表< Foo> list = SharedPools.Default< List&Foo>>().AllocateAndClear();
//使用列表
SharedPools.Default< List&Foo>>()。

//示例3 - 我也看到上述模式的这种变化,其结果与示例1相同,除了示例1似乎创建了一个新的实例IDisposable [PooledObject< T> ] [4]对象。如果您想要更少的GC,这可能是首选的选项。
列表< Foo> list = SharedPools.Default< List&Foo>>().AllocateAndClear();
try
{
//执行某些列表
}
finally
{
SharedPools.Default< List&Foo>> )。免费(列表);
}

2 - ListPool StringBuilderPool - 不是严格的单独的实现,而是围绕SharedPools实现的包装,上面专门为List和StringBuilder显示。所以这会重新使用SharedPools中存储的对象池。

  //示例1  - 不使用语句,所以你需要肯定没有抛出异常。 
StringBuilder stringBuilder = StringBuilderPool.Allocate();
//使用stringBuilder执行某些操作
StringBuilderPool.Free(stringBuilder);

//示例2 - 示例1的更安全版本。
StringBuilder stringBuilder = StringBuilderPool.Allocate();
尝试
{
//使用stringBuilder
}执行某些操作
finally
{
StringBuilderPool.Free(stringBuilder);
}

3 - PooledDictionary PooledHashSet - 这些直接使用ObjectPool,并具有完全独立的对象池。存储128个对象的池。

  //示例1 
PooledHashSet&Foo> hashSet = PooledHashSet&Foo> .GetInstance()
//使用hashSet执行某些操作。
hashSet.Free();

//示例2 - 示例1的更安全版本。
PooledHashSet&Foo> hashSet = PooledHashSet&Foo> .GetInstance()
try
{
//使用hashSet执行某些操作。
}
finally
{
hashSet.Free();
}


Does anyone have a good resource on implementing a shared object pool strategy for a limited resource in vein of Sql connection pooling? (ie would be implemented fully that it is thread safe).

To follow up in regards to @Aaronaught request for clarification the pool usage would be for load balancing requests to an external service. To put it in a scenario that would probably be easier to immediately understand as opposed to my direct situtation. I have a session object that functions similarly to the ISession object from NHibernate. That each unique session manages it's connection to the database. Currently I have 1 long running session object and am encountering issues where my service provider is rate limiting my usage of this individual session.

Due to their lack of expectation that a single session would be treated as a long running service account they apparently treat it as a client that is hammering their service. Which brings me to my question here, instead of having 1 individual session I would create a pool of different sessions and split the requests up to the service across those multiple sessions instead of creating a single focal point as I was previously doing.

Hopefully that background offers some value but to directly answer some of your questions:

Q: Are the objects expensive to create?
A: No objects are a pool of limited resources

Q: Will they be acquired/released very frequently?
A: Yes, once again they can be thought of NHibernate ISessions where 1 is usually acquired and released for the duration of every single page request.

Q: Will a simple first-come-first-serve suffice or do you need something more intelligent, i.e. that would prevent starvation?
A: A simple round robin type distribution would suffice, by starvation I assume you mean if there are no available sessions that callers become blocked waiting for releases. This isn't really applicable since the sessions can be shared by different callers. My goal is distribute the usage across multiple sessions as opposed to 1 single session.

I believe this is probably a divergence from a normal usage of an object pool which is why I originally left this part out and planned just to adapt the pattern to allow sharing of objects as opposed to allowing a starvation situation to ever occur.

Q: What about things like priorities, lazy vs. eager loading, etc.?
A: There is no prioritization involved, for simplicity's sake just assume that I would create the pool of available objects at the creation of the pool itself.

解决方案

Object Pooling in .NET Core

The dotnet core has an implementation of object pooling added to the base class library (BCL). You can read the original GitHub issue here and view the code for System.Buffers. Currently the ArrayPool is the only type available and is used to pool arrays:

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);
    }
}

An example of its usage can be seen in ASP.NET Core. Because it is in the dotnet core BCL, ASP.NET Core can share it's object pool with other objects such as Newtonsoft.Json's JSON serializer. You can read this blog post for more information on how Newtonsoft.Json is doing this.

Object Pooling in Microsoft Roslyn C# Compiler

The new Microsoft Roslyn C# compiler contains the ObjectPool type, which is used to pool frequently used objects which would normally get new'ed up and garbage collected very often. This reduces the amount and size of garbage collection operations which have to happen. There are a few different sub-implementations all using ObjectPool (See: Why are there so many implementations of Object Pooling in Roslyn?).

1 - SharedPools - Stores a pool of 20 objects or 100 if the BigDefault is used.

// 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 - ListPool and StringBuilderPool - Not strictly separate implementations but wrappers around the SharedPools implementation shown above specifically for List and StringBuilder's. So this re-uses the pool of objects stored in 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 - PooledDictionary and PooledHashSet - These use ObjectPool directly and have a totally separate pool of objects. Stores a pool of 128 objects.

// 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();
}

这篇关于C#对象池模式实现的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆