5
\$\begingroup\$

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;
 }
 }
 }
}
Jamal
35.2k13 gold badges134 silver badges238 bronze badges
asked Dec 25, 2016 at 23:09
\$\endgroup\$

1 Answer 1

3
\$\begingroup\$

What you have described here is a Producer- Consumer relationship. .Net has a BlockingCollection that performs exactly what you are trying to create here

public BlockingCollection(
 IProducerConsumerCollection<T> collection,
 int boundedCapacity
)

You could use a ConcurrentBag as the underlying store or any ConcurrentCollection of your choice and the boundedCapacitycan be the maximum number of Tasks that can be added to the collection. An example usage is

BlockingCollection<int> messages = new BlockingCollection<int>(new ConcurrentBag<int>(), 10); 
answered Dec 25, 2016 at 23:23
\$\endgroup\$
5
  • 2
    \$\begingroup\$ I thought of BlockingCollection<T> but in this case how am I going to remove the finished tasks efficiently. On every Add I have to .ToArray() on the blocking collection WaitAny(arrayTask), and then instantiate a new BlockingCollection<T> with the finished task removed, and add the new one added. This seems as a lot of unnecessary operations just for the sake of using the BlockingCollection. Or am I missing some obvious solution? \$\endgroup\$ Commented Dec 26, 2016 at 9:34
  • \$\begingroup\$ @MihailShiahkov I will post an example of how to use the same blocking collection as a producer and a consumer shortly \$\endgroup\$ Commented Dec 26, 2016 at 17:07
  • \$\begingroup\$ The quotes are misleading. It looks like the OP has said that. I suggest not using it for your own words. \$\endgroup\$ Commented Dec 29, 2016 at 14:25
  • \$\begingroup\$ Will update my review @t3chb0t \$\endgroup\$ Commented Dec 29, 2016 at 14:29
  • \$\begingroup\$ I didn't mean removing everything :-o just the quote formattings where applicable :-) \$\endgroup\$ Commented Dec 29, 2016 at 14:49

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.