C#Object Pooling Pattern实现

时间:2010-03-24 19:58:40

标签: c# design-patterns pooling

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

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

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

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

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

问:他们会非常频繁地获得/释放吗? A:是的,可以再次考虑NHibernate ISessions,其中1通常是在每个页面请求期间获取和发布的。

问:一个简单的先来先服务是否足够,或者您是否需要更智能的东西,即可以防止饥饿? A:简单的循环类型分发就足够了,饥饿我认为你的意思是,如果没有可用的会话,呼叫者会被阻塞等待发布。这不是真正适用的,因为会话可以由不同的呼叫者共享。我的目标是在多个会话中分配使用情况,而不是单个会话。

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

问:优先事项,懒惰与急切加载等等有什么关系? A:没有涉及优先级,为简单起见,假设我会在创建池本身时创建可用对象池。

10 个答案:

答案 0 :(得分:304)

答案 1 :(得分:50)

.NET Core中的对象池

dotnet core将对象池的实现添加到基类库(BCL)。您可以阅读原始GitHub问题here并查看System.Buffers的代码。目前,ArrayPool是唯一可用的类型,用于池阵列。有一篇不错的博文here

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核心BCL中,所以ASP.NET Core可以与其他对象(如Newtonsoft.Json的JSON序列化程序)共享它的对象池。您可以阅读this博客文章,了解有关Newtonsoft.Json如何做到这一点的更多信息。

Microsoft Roslyn C#编译器

中的对象池

新的Microsoft Roslyn C#编译器包含ObjectPool类型,该类型用于汇集经常使用的对象,这些对象通常会被新填充并经常收集垃圾。这减少了必须发生的垃圾收集操作的数量和大小。有几个不同的子实现都使用ObjectPool(参见:Why are there so many implementations of Object Pooling in 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工程师设计。阅读博文here或查看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应该声明一次,它将在整个过程中生效 - 这就是池。如果你愿意,可以使用多个游泳池。

答案 2 :(得分:7)

这样的事情可能适合您的需求。

/// <summary>
/// Represents a pool of objects with a size limit.
/// </summary>
/// <typeparam name="T">The type of object in the pool.</typeparam>
public sealed class ObjectPool<T> : IDisposable
    where T : new()
{
    private readonly int size;
    private readonly object locker;
    private readonly Queue<T> queue;
    private int count;


    /// <summary>
    /// Initializes a new instance of the ObjectPool class.
    /// </summary>
    /// <param name="size">The size of the object pool.</param>
    public ObjectPool(int size)
    {
        if (size <= 0)
        {
            const string message = "The size of the pool must be greater than zero.";
            throw new ArgumentOutOfRangeException("size", size, message);
        }

        this.size = size;
        locker = new object();
        queue = new Queue<T>();
    }


    /// <summary>
    /// Retrieves an item from the pool. 
    /// </summary>
    /// <returns>The item retrieved from the pool.</returns>
    public T Get()
    {
        lock (locker)
        {
            if (queue.Count > 0)
            {
                return queue.Dequeue();
            }

            count++;
            return new T();
        }
    }

    /// <summary>
    /// Places an item in the pool.
    /// </summary>
    /// <param name="item">The item to place to the pool.</param>
    public void Put(T item)
    {
        lock (locker)
        {
            if (count < size)
            {
                queue.Enqueue(item);
            }
            else
            {
                using (item as IDisposable)
                {
                    count--;
                }
            }
        }
    }

    /// <summary>
    /// Disposes of items in the pool that implement IDisposable.
    /// </summary>
    public void Dispose()
    {
        lock (locker)
        {
            count = 0;
            while (queue.Count > 0)
            {
                using (queue.Dequeue() as IDisposable)
                {

                }
            }
        }
    }
}

使用示例

public class ThisObject
{
    private readonly ObjectPool<That> pool = new ObjectPool<That>(100);

    public void ThisMethod()
    {
        var that = pool.Get();

        try
        { 
            // Use that ....
        }
        finally
        {
            pool.Put(that);
        }
    }
}

答案 3 :(得分:5)

答案 4 :(得分:4)

当天微软通过Microsoft Transaction Server(MTS)和后来的COM +提供了一个框架来为COM对象做对象池。该功能已转发到.NET Framework中的System.EnterpriseServices,现在已转移到Windows Communication Foundation中。

Object Pooling in WCF

本文来自.NET 1.1,但仍应适用于当前版本的Framework(即使WCF是首选方法)。

Object Pooling .NET

答案 5 :(得分:4)

我非常喜欢Aronaught的实现 - 特别是因为他通过使用信号量来处理等待资源的问题。我想做几个补充:

  1. sync.WaitOne()更改为sync.WaitOne(timeout),并将超时作为Acquire(int timeout)方法的参数公开。当线程超时等待对象变为可用时,这还需要处理条件。
  2. 添加Recycle(T item)方法,以处理在发生故障时需要回收对象的情况,例如。

答案 6 :(得分:3)

面向Java,本文公开了connectionImpl池模式和抽象对象池模式,可能是一个很好的第一种方法: http://www.developer.com/design/article.php/626171/Pattern-Summaries-Object-Pool.htm

对象池模式:

pattern

答案 7 :(得分:3)

这是另一个实现,池中的对象数量有限。

public class ObjectPool<T>
    where T : class
{
    private readonly int maxSize;
    private Func<T> constructor;
    private int currentSize;
    private Queue<T> pool;
    private AutoResetEvent poolReleasedEvent;

    public ObjectPool(int maxSize, Func<T> constructor)
    {
        this.maxSize = maxSize;
        this.constructor = constructor;
        this.currentSize = 0;
        this.pool = new Queue<T>();
        this.poolReleasedEvent = new AutoResetEvent(false);
    }

    public T GetFromPool()
    {
        T item = null;
        do
        {
            lock (this)
            {
                if (this.pool.Count == 0)
                {
                    if (this.currentSize < this.maxSize)
                    {
                        item = this.constructor();
                        this.currentSize++;
                    }
                }
                else
                {
                    item = this.pool.Dequeue();
                }
            }

            if (null == item)
            {
                this.poolReleasedEvent.WaitOne();
            }
        }
        while (null == item);
        return item;
    }

    public void ReturnToPool(T item)
    {
        lock (this)
        {
            this.pool.Enqueue(item);
            this.poolReleasedEvent.Set();
        }
    }
}

答案 8 :(得分:1)

答案 9 :(得分:0)

msdn的扩展,如何使用ConcurrentBag创建对象池。

https://github.com/chivandikwa/ObjectPool