If I use the ThreadPool in a nested way, my application hangs:
ThreadPool.QueueUserWorkItem((state) =>
ThreadPool.QueueUserWorkItem(Action));
One way is using BlockingCollection. This is the class i build for it:
Updated at 2018-04-23:
public class WorkerPool : IDisposable
{
BlockingCollection queue = new BlockingCollection();
List taskList;
private CancellationTokenSource cancellationToken;
int maxWorkers;
private bool wasShutDown;
int waitingUnits;
public WorkerPool(CancellationTokenSource cancellationToken, int maxWorkers)
{
this.cancellationToken = cancellationToken;
this.maxWorkers = maxWorkers;
this.taskList = new List();
}
public void enqueue(T value)
{
queue.Add(value);
waitingUnits++;
}
//call to signal that there are no more item
public void CompleteAdding()
{
queue.CompleteAdding();
}
//create workers and put then running
public void startWorkers(Action worker)
{
for (int i = 0; i < maxWorkers; i++)
{
taskList.Add(new Task(() =>
{
string myname = "worker " + Guid.NewGuid().ToString();
try
{
while (!cancellationToken.IsCancellationRequested)
{
var value = queue.Take();
waitingUnits--;
worker(value);
}
}
catch (Exception ex) when (ex is InvalidOperationException) //throw when collection is closed with CompleteAdding method. No pretty way to do this.
{
//do nothing
}
}));
}
foreach (var task in taskList)
{
task.Start();
}
}
//wait for all workers to be finish their jobs
public void await()
{
while (waitingUnits >0 || !queue.IsAddingCompleted)
Thread.Sleep(100);
shutdown();
}
private void shutdown()
{
wasShutDown = true;
Task.WaitAll(taskList.ToArray());
}
//case something bad happen dismiss all pending work
public void Dispose()
{
if (!wasShutDown)
{
queue.CompleteAdding();
shutdown();
}
}
}
To see it working build this unit test:
[TestMethod]
public void workerPoolTest()
{
WorkerPool workerPool = new WorkerPool(new CancellationTokenSource(), 5);
workerPool.startWorkers(value =>
{
log.Debug(value);
});
for (int i = 0; i < 100; i++)
{
workerPool.enqueue(i);
}
workerPool.CompleteAdding();
workerPool.await();
}
Now you can have as many polls has you like, just by creating new WorkPool objects.
Note that the code is not fully tested.