First of all, I agree with @briantyler: ThreadLocal<T> or static thread fields, probably you want. You should go with this as a starting point and consider other options if this does not fit your needs.
A complex but flexible alternative is the Singleton object pool. In its simplest form, your pool type will look like this:
public sealed class ObjectPool<T> { private readonly ConcurrentQueue<T> __objects = new ConcurrentQueue<T>(); private readonly Func<T> __factory; public ObjectPool(Func<T> factory) { __factory = factory; } public T Get() { T obj; return __objects.TryDequeue(out obj) ? obj : __factory(); } public void Return(T obj) { __objects.Enqueue(obj); } }
This is not very useful if you think of type T in terms of primitive classes or structures (i.e. ObjectPool<MyComponent> ), since there are no built-in flow controls in the pool. But you can replace your type T for the monad Lazy<T> or Task<T> and get exactly what you want.
Pool Initialization:
Func<Task<MyComponent>> factory = () => Task.Run(() => new MyComponent()); ObjectPool<Task<MyComponent>> pool = new ObjectPool<Task<MyComponent>>(factory); // "Pre-warm up" the pool with 16 concurrent tasks. // This starts the tasks on the thread pool and // returns immediately without blocking. for (int i = 0; i < 16; i++) { pool.Return(pool.Get()); }
Using:
// Get a pooled task or create a new one. The task may // have already completed, in which case Result will // be available immediately. If the task is still // in flight, accessing its Result will block. Task<MyComponent> task = pool.Get(); try { MyComponent component = task.Result; // Alternatively you can "await task" // Do something with component. } finally { pool.Return(task); }
This method is more complicated than keeping your component in a static ThreadLocal field or thread, but if you need to do something unusual like limiting the number of merged instances, pool abstraction can be very useful.
EDIT
The basic implementation of a “fixed set of X instances” using Get , which blocks after the pool is drained:
public sealed class ObjectPool<T> { private readonly Queue<T> __objects; public ObjectPool(IEnumerable<T> items) { __objects = new Queue<T>(items); } public T Get() { lock (__objects) { while (__objects.Count == 0) { Monitor.Wait(__objects); } return __objects.Dequeue(); } } public void Return(T obj) { lock (__objects) { __objects.Enqueue(obj); Monitor.Pulse(__objects); } } }
source share