3

I'm trying to manage a list of tasks for a Windows Service so when I shut down the Service, I can use the Task.WaitAll() method to stop the Service from shutting down until all of the remaining tasks complete. For example, I have a Run() method that executes until a boolean is updated:

public void Run()
{
    while (runFlag)
    {
        if (MaxTasksAchieved)
        {
            System.Threading.Thread.Sleep(pollingInterval);
        }
        else
        {

            taskList.Add(Task.Factory.StartNew(() =>
            {
                // do stuff
            }));
        }
    }
}

Then in my Stop() method, I have the following:

public void Stop()
{
    runFlag = false;

    if (taskList.Count > 0)
    {
        // wait
        Task.WaitAll(taskList.ToArray());
    }
    else
    {
        // no wait, great.
    }
}

My question is how do I elegantly have the task remove itself from the list after it's done executing? I want the task to remove itself from the list so when Stop() is called, taskList only contains the tasks that are currently in progress.

farbodg
  • 675
  • 4
  • 14
  • 25
  • 1
    Your `if (MaxTasksAchieved)` and poll loop can be conveniently substituted with a `MaxDegreeOfParallelism` for your task scheduler. – Asad Saeeduddin Feb 12 '15 at 02:07
  • 2
    Regarding removing individual tasks, since you use `WaitAll`, which indicates all tasks have completed, why can't you just discard/ignore the list and move on with what you need to do? Why do you need to empty the list? – Asad Saeeduddin Feb 12 '15 at 02:10
  • @Asad I think I'm missing a key piece of information. So this service is running indefinitely, and as it picks up items to process, it fires off a task and adds it to a list for management purposes. Once the task is complete, I want to remove it from the list so if Stop() is ever called, it only waits for the tasks that are currently in progress. I hope that gives you some more insight into my question. – farbodg Feb 12 '15 at 04:20

4 Answers4

1

Why not just use

public void Stop()
{
   Task.WaitAll(taskList.ToArray());
 }

If task list is empty the service will shutdown immediately.

ziddarth
  • 1,796
  • 1
  • 14
  • 14
  • Right...but I am adding items to my list, and not removing. Won't that be an issue? I should remove the task from the list once it's done processing, unless I am not understanding how the TPL works. Also, what if the service is running indefinitely? Won't my list grow to a massive size and use up all my memory? – farbodg Feb 12 '15 at 04:13
  • 1
    @FarbodGolkar No, you don't have to remove a completed task from the taskList. `Task.WaitAll` waits for all the tasks in the input list to complete, if a task has already completed then there is no need to wait so Task.WaitAll moves onto the next task until all tasks have finished execution. If you want to limit the number of parallel tasks then you should look at using `Parallel.ForEach` and use parallel options described here [MaxDegreeOfParallelism](http://stackoverflow.com/questions/9290498/how-can-i-limit-parallel-foreach) – ziddarth Feb 12 '15 at 04:54
1

Since WaitAll() is waiting until ALL tasks are complete, you can just reinitialize the taskList....

    public void Stop()
    {
        runFlag = false;

        if (taskList.Count > 0)
        {
            // wait
            Task.WaitAll(taskList.ToArray());

            taskList = new List<Task>();
        }
        else
        {
            // no wait, great.
        }
    }
Kaz
  • 721
  • 3
  • 15
1

As others have stated, it's probably unnecessary to remove the task from the list upon completion. But, if you must, you can use a continuation.

Task myTask = new Task(() => {
    // do stuff
});
myTask.ContinueWith(
    t => { taskList.Remove(t); }
);
Ed Noepel
  • 425
  • 3
  • 9
1

If you want to remove the task from the list simply add a continuation that does that:

taskList.Add(task.ContinueWith(t = > taskList.Remove(t)));

But you can simply use Task.WaitAll on all the tasks since the completed tasks are already completed.

Task.WaitAll(taskList.ToArray());

You can easily wait only on the running tasks by filtering the list with the tasks status

Task.WaitAll(taskList.Where(t => t.Status != TaskStatus.RanToCompletion).ToArray());
i3arnon
  • 113,022
  • 33
  • 324
  • 344
  • Say I don't remove tasks from the list, is it possible the list may grow so large that my server will run out of memory? That's my primary concern here. – farbodg Feb 12 '15 at 08:14
  • 1
    @FarbodGolkar Yes. That's possible. Very unlikely, but possible. If that's what you're worried about, then remove the task with `ContinueWith`. – i3arnon Feb 12 '15 at 08:15