I need a collection/bag (something) that will hold some maximum number of running Task
objects. Adding a new running Task
to the collection should block the calling thread (there are many threads trying to add a task so it should be thread safe) until there is an available slot for the new Task
to be added if the maximum number of running tasks is reached. This is what I have so far and it is working fine.
Can you find any problems with this code? Or is there some built-in class that can handle this kind of task?
public class ConcurrentTaskLimiter
{
public int MaxWorkingTasks { get; }
private readonly Task[] _tasks;
private readonly bool[] _finished;
public ConcurrentTaskLimiter(int maxWorkingTasks)
{
MaxWorkingTasks = maxWorkingTasks;
if ((1 <= maxWorkingTasks) == false)
throw new ArgumentOutOfRangeException(nameof(maxWorkingTasks), maxWorkingTasks, "Must be >= 1");
_tasks = new Task[maxWorkingTasks];
_finished = new bool[maxWorkingTasks];
for (int i = 0; i < MaxWorkingTasks; i++)
{
_tasks[i] = Task.FromResult(0); // use this as finished tasks
_finished[i] = true;
}
}
public void BlockAdd(Task t)
{
if (t == null)
throw new ArgumentNullException(nameof(t));
if (t.Status == TaskStatus.Canceled
|| t.Status == TaskStatus.Faulted
|| t.Status == TaskStatus.RanToCompletion)
return;
lock (this)
{
int i;
while (true)
{
for (i = 0; i < MaxWorkingTasks; i++)
{
if (_finished[i])
{
_tasks[i] = t;
_finished[i] = false;
return;
}
}
i = Task.WaitAny(_tasks);
_finished[i] = true;
}
}
}
}