4

I want to create simple infinite loop which feed the job into task based job pool. Also i would like to limit maximum created tasks at the time (i know that task count does not have to be equal to thread count).

What i have is this:

private readonly HashSet<Task> m_runningTasks = new HashSet<Task>();
private const int QueueTaskCount = 10; //our target task count

.... some method here ....

while (m_run){


  IList<Job> jobs = null;

  lock(m_runningTasks){

    //determine missing job count here

    targetCount = QueueTaskCount - m_runningTasks.Count;
    if(targetCount>0)
      jobs = GetSomeWork(targetCount);
  }

  if(jobs != null && jobs.Count > 0){

     //i want to create jobs here in tasks

     foreach(var job in jobs){
       var task = new Task(job.Execute);
       lock(m_runningTasks)
          m_runningTasks.Add(task); //i could add continueTask instead of task here but that does not solve the problem

       var continueTask = task.ContinueWith(x=> {lock(m_runningTasks){
          m_runningTasks.Remove(x);
       }  };)
       task.Run();
     }
    Task[] taskArray;
    lock(m_runningTasks)
        taskArray = m_runningTasks.ToArray()

    Task.WaitAny(taskArray).Wait(); //HERE is the problem

  }


}

I know that when i create continueWith its a new task but i need to block current thread (which create and execute tasks) till some of its tasks inside m_running collection finish. But when i wait for main tasks they could not be removed from collecition (m_runningTasks) yet because continueTask was not finished.

Also somebody suggested continueWith inside this question: Task does not wait for ContinueWith to finish, but that creates new task and i am not waiting for that, but for the original.

My question is How to remove task from collection inside its action (method) AFTER it is finished.

Example:

Lets have MaxDegreeOfParallelism (Aka QueuTaskCount) = 2

So in first iteration we will launch Tasks A and B both with continuation (which removes A and B from the list) lets call them A' and B'.

So in first iteration we have


A->A'

B->B'

in that line Task.WaitAny() line we wait for tasks

A & B


Lets say that A end and B continue

So we iterate again because that Task.WaitAny is fullfiled and we continue to next iteration


When we count, how many tasks we should create now we do that on this line targetCount = QueueTaskCount - m_runningTasks.Count;

As we said A ended (aka runtocomplettiion) B Running

But in the collection (m_runningTasks) we still might or might not have

A & B Or only B - Depends on the task A' (which removes A from the collection)

and here is the problem. In this itteration i dont know what is the state of the collection - but the correct state is only B because A ended.

Community
  • 1
  • 1
LightCZ
  • 695
  • 1
  • 8
  • 20

1 Answers1

0

Well as someone (dont remember know who, deleted answer proposal) suggested this is not the best practice i should go.

You have to solve this problem from the outside - caller or creator of the task is responsible for disposing it. One solution is utilize Task.WhenAny() and remove the finished task inside inner while loop (one at the time), but this is not too much readable nor code nice.

I solved the problem utilizing ActionBlock with MaxDegreeOfParallelism and feeding tasks inside its inner action - as suggested here: Proper way to implement a never ending task. (Timers vs Task)

Community
  • 1
  • 1
LightCZ
  • 695
  • 1
  • 8
  • 20