“Fixed” / “Balanced load balance” C # thread pool?

I have a third-party component that is “expensive” to roll out. This component is not thread safe. The specified component is hosted inside the WCF service (for now), so ... every time a call comes into the service, I have to update the component.

Instead, I would like to have a pool of 16 threads, each of which deploys its own copy of the component and has a method invocation mechanism and distributes it among one of the 16 threads and has a Return Value.

So something simple:

var response = threadPool.CallMethod(param1, param2); 

His penalty for calling is to block until he receives a response, because I need an answer to continue.

Any suggestions? Maybe I'm overdoing it, and ConcurrentQueue , served by 16 threads, will complete the task, but now I'm sure how the return value of the method will be returned to the caller?

+5
source share
2 answers

WCF already uses a thread pool to manage its resources, so if you add a thread control layer, it will only be bad. Avoid doing this if possible, as you get the difference in your office calls.

What would I do in your situation, just use a single ThreadLocal or static thread, which will be initialized by your expensive object once. After that, it will be available for the thread pool thread.

This assumes your object works fine in the MTA stream; I assume this is from your post, as it sounds like it’s ongoing work, but just slow.

There is a fear that too many objects are being created and you are using too much memory because the pool is too large. However, see if this is actually the case before doing anything else. This is a very simple strategy to implement so simple in the trial version. Only get complicated if you really need to.

+2
source

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); } } } 
+2
source

Source: https://habr.com/ru/post/1242677/


All Articles