Right as this is my first post let’s keep it nice and simple.
A pool works by managing a collection of objects kept in memory so they are always available. A pool can be particularly useful for many situations such as managing large classes which take time to initialise and can help contain classes with memory leaks. There is no correct way to make a pool and it should be optimised to the requirements of the situation.
This pool is designed to recycle a collection of objects:
- It creates a new object of type T when it is empty.
- It is important that we only store ONE reference to each instance of an object in the collection, hence the validation in the ReturnToPool() method.
- The lock() method is used to make this class thread safe.
- In most instances such as managing a memory leak, it should be used as a singleton.
- When an object is ready to be returned it is not the responsibility of this class to do so. (I will make a post in the future about correct ways to do this)
The code:
public class Pool < T > { private readonly Stack < T > _pool = new Stack < T > (); private readonly Func < T > _createNewFunction; public Pool(Func < T > createNewFunction) { _createNewFunction = createNewFunction; } public T GetFromPool() { lock(_pool) { if (_pool.Count <= 0) { // Create a new T return _createNewFunction(); } return _pool.Pop(); } } public void ReturnToPool(T item) { lock(_pool) { if (_pool.Contains(item)) { // If it is already in the pool don't re-add it. throw new InvalidOperationException("This item is already in the pool"); } _pool.Push(item); } } }
This is a really useful class; here’s a little feedback:
1. I couldn’t figure out why the class is abstract: it doesn’t declare any abstract methods, and it looks directly useble without needing to be subclassed. (Of course, it could be usefully subclassed, perhaps to work with a T which declares a public default constructor (a : new() constraint) and so which generates the factory delegate for you.)
2. In ReturnToPool, something has surely gone very wrong with a caller if it returns the same item twice: if the caller does that, then in the middle of those two calls that object is available to be handed out to another caller. If it then gets returned again, we can end up with the same item handed out twice. If a caller is returning items multiple times, that’s potentially a very serious bug waiting to bite you – is it appropriate to fail silently? Blowing up if your .Contains call returns true isn’t guaranteed to find all such problems, but it would help find some of them.
3. [minor] How many items are you expecting in the pool – the .Contains call in ReturnToPool is O(n), so a Set might be worth considering as the backing store if the expected count is large.
Great to see this blog arrive (I love the Blend-like styling too) – keep it up!
@Royston Shufflebotham
Good points, the abstract just snuck in there and was not meant to be there and I have amended the class to throw an exception when a duplicate item is returned.
As for point 3. I was expecting it to be a relatively small class, but I shall look into this.
Ah, I see you’ve edited the code to remove the abstract keyword, and get it to throw if an object is already present when returning. Nice one!