Implementación del Patrón de Agrupación de Objetos en C#


¿Alguien tiene un buen recurso para implementar una estrategia de grupo de objetos compartidos para un recurso limitado en la vena de la agrupación de conexiones Sql? (ie se implementaría completamente que es seguro para hilos).

Para dar seguimiento a la solicitud de aclaración de @Aaronaght, el uso del grupo sería para solicitudes de equilibrio de carga a un servicio externo. Para ponerlo en un escenario que probablemente sería más fácil de entender inmediatamente en lugar de mi situación directa. Tengo un objeto de sesión funciona de manera similar al objeto ISession de NHibernate. Que cada sesión única gestiona su conexión a la base de datos. Actualmente tengo 1 objeto de sesión de larga duración y estoy encontrando problemas en los que mi proveedor de servicios está limitando mi uso de esta sesión individual.

Debido a su falta de expectativa de que una sola sesión sería tratada como una cuenta de servicio de larga duración, aparentemente la tratan como un cliente que está martillando su servicio. Lo que me lleva a mi pregunta aquí, en lugar de tener 1 sesión individual, crearía un grupo de sesiones diferentes y dividiría las solicitudes hasta el servicio a través de esas sesiones múltiples en lugar de crear un solo punto focal como lo estaba haciendo anteriormente.

Esperemos que ese fondo ofrezca algún valor, pero para responder directamente a algunas de sus preguntas:

P: ¿Los objetos son caros de crear?
R: Ningún objeto es un conjunto de recursos limitados

P: ¿Serán adquirido / liberado con mucha frecuencia?
R: Sí, una vez más se pueden pensar en ISesiones NHibernate donde 1 se adquiere y libera por la duración de cada solicitud de página.

P: ¿Bastará un simple orden de llegada o se necesita algo más inteligente, es decir, que prevenga el hambre?
R: Una simple distribución de tipo round robin sería suficiente, por inanición asumo que te refieres a si no hay sesiones disponibles que las personas que llaman se bloquean a la espera de liberaciones. Esto no es realmente aplicable ya que las sesiones pueden ser compartidas por diferentes llamantes. Mi objetivo es distribuir el uso a través de varias sesiones en lugar de 1 sola sesión.

Creo que esto es probablemente una divergencia de un uso normal de un conjunto de objetos, por lo que originalmente dejé esta parte fuera y planeé adaptar el patrón para permitir el intercambio de objetos en lugar de permitir que una situación de inanición alguna vez ocurrir.

P: ¿Qué pasa con cosas como prioridades, carga perezosa vs. ansiosa, etc.?
R: No hay priorización involucrada, por simplicidad, solo asuma que crearía el grupo de objetos disponibles en la creación del grupo en sí.

Author: Chris Marisic, 2010-03-24

9 answers

Agrupación de objetos en. NET Core

El dotnet core tiene una implementación de object pooling añadida a la biblioteca de clases base (BCL). Puedes leer el problema original de GitHub aquí y ver el código del sistema .Tampones . Actualmente el ArrayPool es el único tipo disponible y se utiliza para agrupar matrices. Hay una buena entrada de blog aquí.

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

Un ejemplo de su uso se puede ver en ASP.NET Core. Porque está en el núcleo de dotnet BCL, ASP.NET Core puede compartir su grupo de objetos con otros objetos como Newtonsoft.El serializador JSON de Json. Puede leer esta entrada de blog para obtener más información sobre cómo Newtonsoft.Json está haciendo esto.

Agrupación de objetos en Microsoft Roslyn C# Compiler

El nuevo compilador de Microsoft Roslyn C# contiene el tipo ObjectPool, que se utiliza para agrupar objetos de uso frecuente que normalmente obtendrían new'ed up y basura recolectada muy a menudo. Esto reduce la cantidad y el tamaño de operaciones de recolección de basura que tienen que suceder. Hay algunas sub-implementaciones diferentes todas usando ObjectPool (Ver: ¿Por qué hay tantas implementaciones de Object Pooling en Roslyn?).

1 - SharedPools - Almacena un grupo de 20 objetos o 100 si se utiliza BigDefault.

// 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 y StringBuilderPool - No implementaciones estrictamente separadas, sino envoltorios alrededor de la implementación de SharedPools mostrada anteriormente específicamente para List y StringBuilder. Así que esto reutiliza el grupo de objetos almacenados en 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 y PooledHashSet - Estos usan ObjectPool directamente y tienen un grupo de objetos totalmente separado. Almacena un grupo de 128 objetos.

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

De Microsoft.IO.RecyclableMemoryStream

Esta biblioteca proporciona un pooling para los objetos MemoryStream. Es un reemplazo directo para System.IO.MemoryStream. Tiene exactamente la misma semántica. Fue diseñado por ingenieros de Bing. Lea la entrada del blog aquí o vea el código en 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); 
}

Tenga en cuenta que RecyclableMemoryStreamManager debe declararse una vez y vivirá durante todo el proceso–este es el pool. Está perfectamente bien usar múltiples piscinas si lo desea.

 43
Author: Muhammad Rehan Saeed,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-06-21 08:00:02

Esta pregunta es un poco más complicada de lo que uno podría esperar debido a varias incógnitas: El comportamiento del recurso que se está agrupando, la vida útil esperada/requerida de los objetos, la verdadera razón por la que se requiere el grupo, etc. Por lo general, los grupos son grupos de subprocesos de propósito especial, grupos de conexiones, etc. - porque es más fácil optimizar uno cuando se sabe exactamente lo que hace el recurso y lo más importante tiene control sobre cómo se implementa ese recurso.

Ya que no es así de simple, lo que he intentado hacer es ofrecer un enfoque bastante flexible con el que puedas experimentar y ver qué funciona mejor. Disculpas por adelantado por el largo post, pero hay mucho terreno que cubrir cuando se trata de implementar un fondo de recursos decente de propósito general. y realmente sólo estoy arañando la superficie.

Un pool de uso general tendría que tener algunas "configuraciones" principales, incluyendo:

  • Estrategia de carga de recursos-eager or perezoso;
  • Carga de recursos mecanismo - cómo construir realmente uno;
  • Estrategia de acceso - mencionas "round robin" que no es tan sencillo como suena; esta implementación puede usar un búfer circular que es similar , pero no perfecto, porque el grupo no tiene control sobre cuándo se recuperan los recursos. Otras opciones son FIFO y LIFO; FIFO tendrá más de un patrón de acceso aleatorio, pero LIFO hace que sea significativamente más fácil implementar un La estrategia de liberación menos utilizada recientemente (que dijiste que estaba fuera de alcance, pero aún vale la pena mencionarla).

Para el mecanismo de carga de recursos,. NET ya nos da una abstracción limpia: delegados.

private Func<Pool<T>, T> factory;

Pasa esto a través del constructor del pool y estamos a punto de terminar con eso. Usar un tipo genérico con una restricción new() también funciona, pero esto es más flexible.


De los otros dos parámetros, la estrategia de acceso es la más complicada bestia, por lo que mi enfoque fue utilizar un enfoque basado en la herencia (interfaz):

public class Pool<T> : IDisposable
{
    // Other code - we'll come back to this

    interface IItemStore
    {
        T Fetch();
        void Store(T item);
        int Count { get; }
    }
}

El concepto aquí es simple: dejaremos que la clase pública Pool maneje los problemas comunes como la seguridad del hilo, pero usaremos un "almacén de artículos" diferente para cada patrón de acceso. LIFO se representa fácilmente mediante una pila, FIFO es una cola, y he utilizado una implementación de búfer circular no muy optimizada, pero probablemente adecuada, utilizando un List<T> y un puntero de índice para aproximar un acceso round-robin patrón.

Todas las clases a continuación son clases internas de la Pool<T> - esta fue una elección de estilo, pero ya que estas realmente no están destinadas a ser utilizadas fuera de la Pool, tiene más sentido.

    class QueueStore : Queue<T>, IItemStore
    {
        public QueueStore(int capacity) : base(capacity)
        {
        }

        public T Fetch()
        {
            return Dequeue();
        }

        public void Store(T item)
        {
            Enqueue(item);
        }
    }

    class StackStore : Stack<T>, IItemStore
    {
        public StackStore(int capacity) : base(capacity)
        {
        }

        public T Fetch()
        {
            return Pop();
        }

        public void Store(T item)
        {
            Push(item);
        }
    }

Estos son los obvios - pila y cola. No creo que merezcan mucha explicación. El buffer circular es un poco más complicado:

    class CircularStore : IItemStore
    {
        private List<Slot> slots;
        private int freeSlotCount;
        private int position = -1;

        public CircularStore(int capacity)
        {
            slots = new List<Slot>(capacity);
        }

        public T Fetch()
        {
            if (Count == 0)
                throw new InvalidOperationException("The buffer is empty.");

            int startPosition = position;
            do
            {
                Advance();
                Slot slot = slots[position];
                if (!slot.IsInUse)
                {
                    slot.IsInUse = true;
                    --freeSlotCount;
                    return slot.Item;
                }
            } while (startPosition != position);
            throw new InvalidOperationException("No free slots.");
        }

        public void Store(T item)
        {
            Slot slot = slots.Find(s => object.Equals(s.Item, item));
            if (slot == null)
            {
                slot = new Slot(item);
                slots.Add(slot);
            }
            slot.IsInUse = false;
            ++freeSlotCount;
        }

        public int Count
        {
            get { return freeSlotCount; }
        }

        private void Advance()
        {
            position = (position + 1) % slots.Count;
        }

        class Slot
        {
            public Slot(T item)
            {
                this.Item = item;
            }

            public T Item { get; private set; }
            public bool IsInUse { get; set; }
        }
    }

Podría haber elegido una serie de enfoques diferentes, pero la conclusión es que se debe acceder a los recursos en el mismo orden en que fueron creados, lo que significa que tenemos que mantener referencias a ellos pero marcarlos como "en uso" (o no). En el peor de los casos, solo hay una ranura disponible, y se necesita una iteración completa del búfer para cada búsqueda. Esto es malo si tiene cientos de recursos agrupados y los está adquiriendo y liberando varias veces por segundo; en realidad no es un problema para un grupo de 5-10 elementos, y en el caso típico , donde los recursos se usan ligeramente, solo tiene que avanzar una o dos ranuras.

Recuerde, estas clases son clases internas privadas - es por eso que no necesitan una gran cantidad de comprobación de errores, el grupo en sí restringe el acceso a ellas.

Agregue una enumeración y un método de fábrica y terminamos con esta parte:{[44]]}

// Outside the pool
public enum AccessMode { FIFO, LIFO, Circular };

    private IItemStore itemStore;

    // Inside the Pool
    private IItemStore CreateItemStore(AccessMode mode, int capacity)
    {
        switch (mode)
        {
            case AccessMode.FIFO:
                return new QueueStore(capacity);
            case AccessMode.LIFO:
                return new StackStore(capacity);
            default:
                Debug.Assert(mode == AccessMode.Circular,
                    "Invalid AccessMode in CreateItemStore");
                return new CircularStore(capacity);
        }
    }

El siguiente problema a resolver es la estrategia de carga. He definido tres tipos:

public enum LoadingMode { Eager, Lazy, LazyExpanding };

Los dos primeros deben explicarse por sí mismos; el tercero es una especie de híbrido, carga perezosa recursos, pero en realidad no comienza a reutilizar ningún recurso hasta que el grupo está lleno. Esto sería una buena compensación si desea que el grupo esté lleno (lo que parece que sí), pero desea aplazar el gasto de crearlos hasta el primer acceso (es decir, para mejorar los tiempos de inicio).

Los métodos de carga realmente no son demasiado complicados, ahora que tenemos la abstracción de la tienda de artículos:

    private int size;
    private int count;

    private T AcquireEager()
    {
        lock (itemStore)
        {
            return itemStore.Fetch();
        }
    }

    private T AcquireLazy()
    {
        lock (itemStore)
        {
            if (itemStore.Count > 0)
            {
                return itemStore.Fetch();
            }
        }
        Interlocked.Increment(ref count);
        return factory(this);
    }

    private T AcquireLazyExpanding()
    {
        bool shouldExpand = false;
        if (count < size)
        {
            int newCount = Interlocked.Increment(ref count);
            if (newCount <= size)
            {
                shouldExpand = true;
            }
            else
            {
                // Another thread took the last spot - use the store instead
                Interlocked.Decrement(ref count);
            }
        }
        if (shouldExpand)
        {
            return factory(this);
        }
        else
        {
            lock (itemStore)
            {
                return itemStore.Fetch();
            }
        }
    }

    private void PreloadItems()
    {
        for (int i = 0; i < size; i++)
        {
            T item = factory(this);
            itemStore.Store(item);
        }
        count = size;
    }

Los campos size y count anteriores se refieren al tamaño máximo del pool y número total de recursos de propiedad del consorcio (pero no necesariamente disponibles ), respectivamente. AcquireEager es el más simple, asume que un elemento ya está en la tienda - estos elementos se precargarían en la construcción, es decir, en el método PreloadItems mostrado al final.

AcquireLazy comprueba si hay elementos gratis en el grupo, y si no, crea uno nuevo. AcquireLazyExpanding creará un nuevo recurso siempre y cuando el grupo no haya alcanzado su tamaño objetivo todavía. He tratado de optimizar esto para minimice el bloqueo, y espero no haber cometido ningún error ( he probado esto bajo condiciones multihilo, pero obviamente no exhaustivamente).

Es posible que se pregunte por qué ninguno de estos métodos se molesta en verificar si la tienda ha alcanzado el tamaño máximo. Llegaré a eso en un momento.


Ahora para la piscina en sí. Aquí está el conjunto completo de datos privados, algunos de los cuales ya se han mostrado:

    private bool isDisposed;
    private Func<Pool<T>, T> factory;
    private LoadingMode loadingMode;
    private IItemStore itemStore;
    private int size;
    private int count;
    private Semaphore sync;

Respondiendo a la pregunta I pasado por alto en el último párrafo - cómo asegurar que limitamos el número total de recursos creados-resulta que el.NET ya tiene una herramienta perfectamente buena para eso, se llama Semaphore y está diseñado específicamente para permitir un número fijo de subprocesos de acceso a un recurso (en este caso el "recurso" es el almacén de elementos interno). Dado que no estamos implementando una cola de productor/consumidor completa, esto es perfectamente adecuado para nuestras necesidades.

El constructor parece esto:

    public Pool(int size, Func<Pool<T>, T> factory,
        LoadingMode loadingMode, AccessMode accessMode)
    {
        if (size <= 0)
            throw new ArgumentOutOfRangeException("size", size,
                "Argument 'size' must be greater than zero.");
        if (factory == null)
            throw new ArgumentNullException("factory");

        this.size = size;
        this.factory = factory;
        sync = new Semaphore(size, size);
        this.loadingMode = loadingMode;
        this.itemStore = CreateItemStore(accessMode, size);
        if (loadingMode == LoadingMode.Eager)
        {
            PreloadItems();
        }
    }

No debería haber sorpresas aquí. Lo único a tener en cuenta es la carcasa especial para la carga ansiosa, utilizando el método PreloadItems ya mostrado anteriormente.

Dado que casi todo ha sido limpiamente abstraído por ahora, los métodos reales Acquire y Release son realmente muy sencillos: {[44]]}

    public T Acquire()
    {
        sync.WaitOne();
        switch (loadingMode)
        {
            case LoadingMode.Eager:
                return AcquireEager();
            case LoadingMode.Lazy:
                return AcquireLazy();
            default:
                Debug.Assert(loadingMode == LoadingMode.LazyExpanding,
                    "Unknown LoadingMode encountered in Acquire method.");
                return AcquireLazyExpanding();
        }
    }

    public void Release(T item)
    {
        lock (itemStore)
        {
            itemStore.Store(item);
        }
        sync.Release();
    }

Como se explicó anteriormente, estamos usando el Semaphore para controlar la concurrencia en lugar de verificar religiosamente el estado del almacén de artículos. Siempre que los artículos adquiridos sean correctamente liberado, no hay nada de qué preocuparse.

Por último, pero no menos importante, hay limpieza:

    public void Dispose()
    {
        if (isDisposed)
        {
            return;
        }
        isDisposed = true;
        if (typeof(IDisposable).IsAssignableFrom(typeof(T)))
        {
            lock (itemStore)
            {
                while (itemStore.Count > 0)
                {
                    IDisposable disposable = (IDisposable)itemStore.Fetch();
                    disposable.Dispose();
                }
            }
        }
        sync.Close();
    }

    public bool IsDisposed
    {
        get { return isDisposed; }
    }

El propósito de esa propiedad IsDisposed se aclarará en un momento. Todo lo que el método principal Dispose realmente hace es desechar los elementos agrupados reales si implementan IDisposable.


Ahora básicamente puedes usar esto tal cual, con un bloque try-finally, pero no me gusta esa sintaxis, porque si empiezas a pasar recursos agrupados entre clases y métodos, entonces es va a ser muy confuso. Es posible que la clase principal que usa un recurso ni siquiera tenga una referencia al pool. Realmente se vuelve bastante desordenado, por lo que un mejor enfoque es crear un objeto agrupado "inteligente".

Digamos que comenzamos con la siguiente interfaz/clase simple:

public interface IFoo : IDisposable
{
    void Test();
}

public class Foo : IFoo
{
    private static int count = 0;

    private int num;

    public Foo()
    {
        num = Interlocked.Increment(ref count);
    }

    public void Dispose()
    {
        Console.WriteLine("Goodbye from Foo #{0}", num);
    }

    public void Test()
    {
        Console.WriteLine("Hello from Foo #{0}", num);
    }
}

Aquí está nuestro recurso desechable ficticio Foo que implementa IFoo y tiene algún código repetitivo para generar identidades únicas. Lo que hacemos es crear otro objeto especial, agrupado:

public class PooledFoo : IFoo
{
    private Foo internalFoo;
    private Pool<IFoo> pool;

    public PooledFoo(Pool<IFoo> pool)
    {
        if (pool == null)
            throw new ArgumentNullException("pool");

        this.pool = pool;
        this.internalFoo = new Foo();
    }

    public void Dispose()
    {
        if (pool.IsDisposed)
        {
            internalFoo.Dispose();
        }
        else
        {
            pool.Release(this);
        }
    }

    public void Test()
    {
        internalFoo.Test();
    }
}

Esto solo proxies todos los métodos "reales" a su interior IFoo (podríamos hacer esto con una biblioteca de Proxy Dinámico como Castle, pero no entraré en eso). También mantiene una referencia al Pool que lo crea, de modo que cuando Dispose este objeto, se libera automáticamente al pool. Excepto cuando el pool ya ha sido eliminado - esto significa que estamos en modo "cleanup" y en este caso realmente limpia recurso interno en su lugar.


Usando el enfoque anterior, podemos escribir código como este:

// Create the pool early
Pool<IFoo> pool = new Pool<IFoo>(PoolSize, p => new PooledFoo(p),
    LoadingMode.Lazy, AccessMode.Circular);

// Sometime later on...
using (IFoo foo = pool.Acquire())
{
    foo.Test();
}

Esta es una muy buena cosa para poder hacer. Esto significa que el código que usa el IFoo (a diferencia del código que lo crea) en realidad no necesita ser consciente del pool. Incluso puede inyectarse IFoo objetos usando su biblioteca DI favorita y el Pool<T> como proveedor/fábrica.


He puesto el complete el código en PasteBin para su disfrute de copiar y pegar. También hay un corto programa de prueba que puedes usar para jugar con diferentes modos de carga/acceso y condiciones multiproceso, para asegurarte de que es seguro para subprocesos y no tiene errores.

Hágame saber si tiene alguna pregunta o inquietud sobre cualquiera de esto.

 294
Author: Aaronaught,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-04-03 22:40:11

Algo como esto podría satisfacer sus necesidades.

/// <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)
                {

                }
            }
        }
    }
}

Ejemplo De Uso

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);
        }
    }
}
 7
Author: ChaosPandion,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-04-02 23:22:45
 5
Author: Thomas Mutzl,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-05-18 20:43:17

En el pasado, Microsoft proporcionó un marco a través de Microsoft Transaction Server (MTS) y más tarde COM+ para hacer una agrupación de objetos para objetos COM. Esa funcionalidad se transfirió al Sistema.EnterpriseServices en. NET Framework y ahora en Windows Communication Foundation.

Agrupación de objetos en WCF

Este artículo es de. NET 1.1, pero aún debe aplicarse en las versiones actuales del Framework (a pesar de que WCF es el preferido método).

Object Pooling. NET

 4
Author: Thomas,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-04-02 00:47:59

Me gusta mucho la implementación de Aronaght especially especialmente porque maneja la espera de que el recurso esté disponible a través del uso de un semáforo. Hay varias adiciones que me gustaría hacer:

  1. Cambie sync.WaitOne() a sync.WaitOne(timeout) y exponga el tiempo de espera como parámetro en el método Acquire(int timeout). Esto también requeriría manejar la condición cuando el hilo se agota esperando que un objeto esté disponible.
  2. Add Recycle(T item) método para manejar situaciones cuando un objeto necesita ser reciclado cuando se produce un fallo, por ejemplo.
 4
Author: Igor Pashchuk,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-08-23 20:27:12

Esta es otra implementación, con un número limitado de objetos en pool.

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();
        }
    }
}
 3
Author: Peter K.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-04-10 15:48:42

Orientado a Java, este artículo expone el patrón de grupo connectionImpl y el patrón de grupo de objetos abstraídos y podría ser un buen primer enfoque : http://www.developer.com/design/article.php/626171/Pattern-Summaries-Object-Pool.htm

Patrón de grupo de objetos:

patrón

 3
Author: JoeBilly,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-09-29 05:23:42

Una extensión de cómo crear un grupo de objetos usando un ConcurrentBag de msdn.

Https://github.com/chivandikwa/ObjectPool

 0
Author: Thulani Chivandikwa,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-06-30 11:58:17