Implementacja C# Object Pooling Pattern

Czy ktoś ma dobry zasób na implementację strategii współdzielonej puli obiektów dla ograniczonego zasobu w stylu poolingu połączeń Sql? (czyli będzie w pełni zaimplementowany, że jest bezpieczny dla wątku).

W związku z @ aaronaught Prośba o wyjaśnienie użycie puli byłoby dla żądań równoważenia obciążenia do zewnętrznej usługi. Ująć to w scenariusz, który prawdopodobnie byłby łatwiejszy do natychmiastowego zrozumienia, w przeciwieństwie do mojej bezpośredniej situtacji. Mam obiekt sesji, który działa podobnie do obiektu ISession z NHibernate. Że każda unikalna sesja zarządza połączeniem z bazą danych. Obecnie mam 1 długo działający obiekt sesji i napotykam problemy, w których Mój dostawca usług ogranicza moje wykorzystanie tej pojedynczej sesji.

Ze względu na brak oczekiwań, że pojedyncza sesja będzie traktowana jako długo działające konto usługowe, najwyraźniej traktują je jako klienta, który hamuje ich usługę. Co sprowadza mnie do mojego pytania tutaj, zamiast mieć 1 indywidualną sesję, stworzyłbym pulę różnych sesji i podzielił żądania do usługi na te wiele sesji, zamiast tworzyć pojedynczy punkt centralny, jak to robiłem wcześniej.

Mam nadzieję, że tło oferuje jakąś wartość, ale bezpośrednio odpowiedzieć na niektóre z twoich pytań:

P: czy tworzenie obiektów jest kosztowne?
O: żadne obiekty nie są pulą ograniczonych zasobów

P: czy będą pozyskiwane / wypuszczane bardzo często?
O: tak, po raz kolejny można pomyśleć o NHibernate is, gdzie 1 jest zwykle nabywane i wydawane na czas trwania każdego pojedynczego żądania strony.

P: czy wystarczy prosty "kto pierwszy ten lepszy", czy też potrzebujesz czegoś bardziej inteligentnego, tzn. zapobiegającego głodowi?
A: prosty rozkład typu round robin wystarczyłby, przez głodzenie zakładam, że masz na myśli, jeśli nie ma dostępnych sesji, które rozmówcy zostają zablokowani w oczekiwaniu na Wydania. Nie ma to zastosowania, ponieważ sesje mogą być współdzielone przez różnych rozmówców. Moim celem jest rozdzielenie użycia na wiele sesji, a nie na jedną sesję.

Uważam, że jest to prawdopodobnie rozbieżność od normalnego korzystania z puli obiektów, dlatego pierwotnie pominąłem tę część i planowałem tylko dostosować wzór, aby umożliwić dzielenie się obiektami, w przeciwieństwie do sytuacji głodu występuje.

P: a co z priorytetami, leniwym vs. chętnym ładowaniem itp.?
O: nie ma priorytetyzacji, dla uproszczenia po prostu Załóżmy, że stworzyłbym pulę dostępnych obiektów przy tworzeniu samej puli.

Author: Chris Marisic, 2010-03-24

9 answers

Łączenie obiektów w. NET Core

Rdzeń dotnet posiada implementację poolingu obiektów dodaną do biblioteki klas bazowych (BCL). Możesz przeczytać oryginalny numer GitHub tutaj i wyświetlić kod dla systemu.Bufory . Obecnie ArrayPool jest jedynym dostępnym typem i jest używany do łączenia tablic. Jest tu fajny wpis na blogu .

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

Przykład jego użycia można zobaczyć w ASP.NET Rdzeń. Ponieważ jest w dotnet core BCL, ASP.NET Core może udostępniać swoją pulę obiektów z innymi obiektami, takimi jak Newtonsoft.JSON ' s JSON serializer. Możesz przeczytać ten blogu, aby uzyskać więcej informacji na temat jak Newtonsoft.Json to robi.

Łączenie obiektów w kompilatorze Microsoft C#

Nowy kompilator Microsoft Roslyn C # zawiera Typ ObjectPool , który jest używany do łączenia często używanych obiektów, które zwykle są nowe i często zbierane śmieci. Zmniejsza to ilość i wielkość operacje zbierania śmieci, które muszą się wydarzyć. Istnieje kilka różnych pod-implementacji, które używają ObjectPool (Zobacz: dlaczego jest tak wiele implementacji poolingu obiektów w Roslyn?).

1 - SharedPools - przechowuje pulę 20 obiektów lub 100 jeśli BigDefault jest używany.

// 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 i StringBuilderPool - nie są to ściśle oddzielne implementacje, ale owijki wokół implementacji SharedPools pokazanej powyżej specjalnie dla List i StringBuilder' s. Więc to ponownie wykorzystuje pulę obiektów przechowywanych w 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 i PooledHashSet - używają one obiektu ObjectPool bezpośrednio i mają całkowicie oddzielną pulę obiektów. Przechowuje pulę 128 obiektów.

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

Ta biblioteka zapewnia pooling dla MemoryStream obiektów. To zamiennik System.IO.MemoryStream. Ma dokładnie tę samą semantykę. Został zaprojektowany przez inżynierów Bing. Przeczytaj wpis na blogu tutaj lub zobacz kod na 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); 
}

Zauważ, że RecyclableMemoryStreamManager powinno być zadeklarowane raz i będzie żyć przez cały proces–jest to Pula. Jest to całkowicie w porządku, aby korzystać z wielu basenów, jeśli chcesz.

 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/doraprojects.net/template/agent.layouts/content.php on line 54
2017-06-21 08:00:02

To pytanie jest nieco trudniejsze, niż można by się spodziewać ze względu na kilka niewiadomych: zachowanie zasobu jest łączony, oczekiwany / wymagany czas życia obiektów, prawdziwy powód, dla którego pula jest wymagana, itp. Zazwyczaj baseny to baseny specjalnego przeznaczenia-baseny gwintowane, baseny połączeniowe itp. - ponieważ łatwiej jest zoptymalizować, gdy dokładnie wiesz, co robi zasób i co ważniejsze, masz kontrolę nad tym, jak ten zasób jest zaimplementowany.

Ponieważ nie jest to proste, co próbowałem zrobić, to zaoferować dość elastyczne podejście, z którym można eksperymentować i zobaczyć, co działa najlepiej. Z góry przepraszam za długi post, ale jest wiele do omówienia, jeśli chodzi o wdrożenie przyzwoitej puli zasobów ogólnego przeznaczenia. a ja tylko drapię powierzchnię.

W tym celu należy wykonać następujące czynności:]}
  • Strategia ładowania zasobów-chętny lub leniwy;
  • Resource loading mechanism - how to really construct one;
  • strategia dostępu-wspominasz "round robin", który nie jest tak prosty, jak się wydaje; Ta implementacja może używać okrągłego bufora, który jest podobny , ale nie doskonały, ponieważ pula nie ma kontroli nad tym, kiedy zasoby są faktycznie odzyskiwane. Inne opcje to FIFO i LIFO; FIFO będzie miało więcej wzorca dostępu losowego, ale LIFO znacznie ułatwia implementację Najmniej-Ostatnio-stosowana strategia uwalniania (o której mówiłeś, że jest poza zakresem, ale wciąż warto o niej wspomnieć).

Dla mechanizmu ładowania zasobów,. NET już daje nam czystą abstrakcję-delegatów.

private Func<Pool<T>, T> factory;
Przepuść to przez konstruktora basenu i kończymy z tym. Użycie typu ogólnego z ograniczeniem new() działa również, ale jest to bardziej elastyczne.

Z pozostałych dwóch parametrów, strategia dostępu jest bardziej skomplikowana w związku z tym, że nie jest to możliwe, nie jest to możliwe.]}

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

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

Koncepcja jest prosta - pozwolimy klasie public Pool zająć się typowymi kwestiami, takimi jak bezpieczeństwo wątków, ale użyjemy innego" sklepu z przedmiotami " dla każdego wzorca dostępu. LIFO jest łatwo reprezentowane przez stos, FIFO jest kolejką, a ja użyłem niezbyt zoptymalizowanej, ale prawdopodobnie odpowiedniej, okrągłej implementacji bufora przy użyciu wskaźnika List<T> i wskaźnika indeksu, aby przybliżać dostęp round-robin wzór.

Wszystkie z poniższych klas są wewnętrznymi klasami Pool<T> - to był wybór stylu, ale ponieważ te naprawdę nie są przeznaczone do użycia poza Pool, to ma największy sens.

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

To są te oczywiste-stack i queue. Nie sądzę, żeby zasługiwały na wiele wyjaśnień. Bufor Okrągły jest nieco bardziej skomplikowany:

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

Mogłem wybrać wiele różnych podejść, ale najważniejsze jest to, że zasoby powinny być dostępne w tej samej kolejności, w jakiej zostały stworzone, co oznacza, że musimy zachować odniesienia do nich, ale oznaczyć je jako "w użyciu" (lub nie). W najgorszym przypadku tylko jeden slot jest zawsze dostępny, a każde pobieranie wymaga pełnej iteracji bufora. Jest to złe, jeśli masz setki zasobów połączonych i nabywasz je i uwalniasz kilka razy na sekundę; tak naprawdę nie jest to problem dla puli 5-10 przedmiotów, a w typowym przypadku, gdzie zasoby są lekko używane, to tylko musi przejść jeden lub dwa sloty.

Pamiętaj, że te klasy są prywatnymi klasami wewnętrznymi - dlatego nie wymagają sprawdzania błędów, sama pula ogranicza do nich dostęp.

W tym celu należy wykonać następujące czynności:]}
// 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);
        }
    }

Kolejnym problemem do rozwiązania jest wczytywanie strategii. Zdefiniowałem trzy typy:

public enum LoadingMode { Eager, Lazy, LazyExpanding };

Pierwsze dwa powinny być oczywiste; trzeci jest rodzajem hybrydy, jest leniwy-ładuje zasoby, ale w rzeczywistości nie zaczyna ponownie wykorzystywać żadnych zasobów, dopóki pula nie jest pełna. Byłoby to dobrym kompromisem, jeśli chcesz, aby pula była pełna (co brzmi jak robisz), ale chcesz odroczyć koszty rzeczywistego ich tworzenia do pierwszego dostępu(tj.

Metody ładowania naprawdę nie są zbyt skomplikowane, teraz, gdy mamy abstrakcję item-store:]}
    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;
    }

Pola size i count powyżej odnoszą się do maksymalnego rozmiaru puli i łączna liczba zasobów należących do grupy (ale niekoniecznie dostępnych), odpowiednio. AcquireEager jest najprostsza, zakłada, że element jest już w sklepie - elementy te zostaną wstępnie załadowane podczas budowy, czyli w metodzie PreloadItems pokazanej na końcu.

AcquireLazy sprawdza, czy w Puli są wolne przedmioty, a jeśli nie, tworzy nowy. AcquireLazyExpanding utworzy nowy zasób tak długo, jak Pula nie osiągnie jeszcze docelowego rozmiaru. Próbowałem zoptymalizować to do zminimalizuj blokowanie i mam nadzieję, że nie popełniłem żadnych błędów(przetestowałem to w Warunkach wielowątkowych, ale oczywiście nie wyczerpująco).

Możesz się zastanawiać, dlaczego żadna z tych metod nie sprawdza, czy sklep osiągnął maksymalny rozmiar. Zaraz do tego dojdę.


Teraz sam basen. Oto pełny zestaw prywatnych danych, z których niektóre zostały już pokazane:
    private bool isDisposed;
    private Func<Pool<T>, T> factory;
    private LoadingMode loadingMode;
    private IItemStore itemStore;
    private int size;
    private int count;
    private Semaphore sync;

Odpowiadając na pytanie I omówione w ostatnim akapicie - jak zapewnić ograniczenie całkowitej liczby utworzonych zasobów - okazuje się, że.NET ma już doskonałe narzędzie do tego, nazywa się semaforem i został zaprojektowany specjalnie, aby umożliwić dostęp do zasobu określonej liczbie wątków (w tym przypadku "zasobem" jest wewnętrzny magazyn przedmiotów). Ponieważ nie wdrażamy pełnej kolejki producentów/konsumentów, jest to całkowicie odpowiednie dla naszych potrzeb.

Konstruktor wygląda jak to:

    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();
        }
    }
Nie powinno być niespodzianek. Jedyną rzeczą na uwagę jest specjalna obudowa do chętnego ładowania, przy użyciu metody PreloadItems pokazanej wcześniej.

Ponieważ prawie wszystko zostało już czysto abstrakcyjne, rzeczywiste metody Acquire i {27]} są naprawdę bardzo proste: {]}

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

Jak wyjaśniono wcześniej, używamy Semaphore do kontrolowania współbieżności zamiast religijnego sprawdzania statusu sklepu z przedmiotami. Tak długo, jak nabywane przedmioty są poprawnie wydany, nie ma się czym martwić.

Last but not least, there ' s cleanup:

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

Cel tej własności IsDisposed stanie się jasny Za chwilę. Główną metodą Dispose jest pozbycie się faktycznie zgromadzonych przedmiotów, jeśli zaimplementują IDisposable.


Teraz możesz w zasadzie używać tego jako-is, Z try-finally blokiem, ale nie przepadam za tą składnią, ponieważ jeśli zaczniesz przekazywać zasoby między klasami i metodami, to będzie bardzo mylące. Możliwe, że główna klasa, która używa zasobu, nawet nie ma odniesienia do puli. To naprawdę staje się dość niechlujny, więc lepszym podejściem jest stworzenie "inteligentnego" obiektu zbiorczego.

Powiedzmy, że zaczynamy od następującego prostego interfejsu/klasy:

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

Oto nasz pozornie jednorazowy Foo zasób, który implementuje IFoo i ma kod do generowania unikalnych tożsamości. To, co robimy, to tworzenie kolejnego specjalny, zbiorczy obiekt:

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

To po prostu proxy wszystkich "prawdziwych" metod do swojego wnętrza IFoo (możemy to zrobić z dynamiczną biblioteką Proxy, taką jak Castle, ale nie będę się w to mieszać). Utrzymuje również odniesienie do Pool, który go tworzy, tak, że gdy {[30] } ten obiekt, automatycznie zwalnia się z powrotem do puli. z wyjątkiem kiedy basen został już usunięty-oznacza to, że jesteśmy w trybie" cleanup " i w tym przypadku faktycznie czyści zamiast tego zasób wewnętrzny .


Korzystając z powyższego podejścia, możemy napisać kod w następujący sposób:

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

To jestbardzo dobra rzecz, aby móc to zrobić. Oznacza to, że kod, który używa IFoo (W przeciwieństwie do kodu, który go tworzy), nie musi być świadomy puli. Można nawet wstrzyknąć IFoo obiekty korzystające z ulubionej biblioteki DI i Pool<T> jako dostawcy / fabryki.


I ' ve put the uzupełnij kod na Pastebinie dla przyjemności kopiowania i wklejania. Istnieje również krótki program testowy, którego możesz użyć do zabawy z różnymi trybami ładowania/dostępu i wielowątkowymi warunkami, aby upewnić się, że jest bezpieczny dla wątków i nie jest wadliwy.

Daj mi znać, jeśli masz jakieś pytania lub wątpliwości dotyczące tego.
 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/doraprojects.net/template/agent.layouts/content.php on line 54
2010-04-03 22:40:11

Coś takiego może pasować do Twoich potrzeb.

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

                }
            }
        }
    }
}

Przykład Użycia

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/doraprojects.net/template/agent.layouts/content.php on line 54
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/doraprojects.net/template/agent.layouts/content.php on line 54
2015-05-18 20:43:17

Kiedyś Microsoft dostarczał framework za pośrednictwem Microsoft Transaction Server (MTS), a później COM+ do łączenia obiektów dla obiektów COM. Funkcjonalność ta została przeniesiona do systemu.EnterpriseServices w. NET Framework, a teraz w Windows Communication Foundation.

Pooling obiektów w WCF

Ten artykuł pochodzi z. NET 1.1, ale powinien nadal obowiązywać w obecnych wersjach frameworka (nawet jeśli WCF jest preferowanym metoda).

Obiekt 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/doraprojects.net/template/agent.layouts/content.php on line 54
2010-04-02 00:47:59

Bardzo podoba mi się implementacja Aronaught-zwłaszcza, że zajmuje się on czekaniem na udostępnienie zasobu za pomocą semafora. Jest kilka dodatków, które chciałbym zrobić:

  1. Zmień sync.WaitOne() na sync.WaitOne(timeout) i wyeksponuj timeout jako parametr w metodzie Acquire(int timeout). Wymagałoby to również obsługi warunku, gdy wątek przekroczy czas oczekiwania na obiekt, aby stał się dostępny.
  2. Dodaj metodę Recycle(T item) do obsługi sytuacji, w których obiekt musi być recykling, gdy wystąpi awaria, na przykład.
 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/doraprojects.net/template/agent.layouts/content.php on line 54
2010-08-23 20:27:12

Jest to kolejna implementacja z ograniczoną liczbą obiektów w Puli.

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/doraprojects.net/template/agent.layouts/content.php on line 54
2013-04-10 15:48:42

Zorientowany na Javę, ten artykuł ujawnia wzorzec puli connectionImpl i abstrakcyjny wzorzec puli obiektów i może być dobrym pierwszym podejściem : http://www.developer.com/design/article.php/626171/Pattern-Summaries-Object-Pool.htm

Wzór puli obiektów:

wzór

 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/doraprojects.net/template/agent.layouts/content.php on line 54
2014-09-29 05:23:42

Rozszerzenie MSDN jak utworzyć pulę obiektów przy użyciu współbieżnego worka.

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/doraprojects.net/template/agent.layouts/content.php on line 54
2015-06-30 11:58:17