Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

When to dispose of System.Threading.Task with child tasks?

I have a task that launches several child tasks. (e.g., Task A creates B,C,D,E,F). I also create a System.Threading.Timer to poll a database every 10 seconds to check if the scheduled item was cancelled by request. If it does, it sets CancellationTokenSource so that the task knows to cancel. Each sub-task, in this case B,C,D,E,F, will cancel when appropriate (they are looping thru files and moving them around).

Since Task implements IDisposable, I want to know if it is a good idea to call Task.WaitAll again from the catch block, to wait for the cancellations to propogate. While the cancellation request will be processed, the sub-tasks may be in the middle of a loop and can't cancel until that completes

However, per MSDN:

Always call Dispose before you release your last reference to the Task. Otherwise, the resources it is using will not be freed until the garbage collector calls the Task object's Finalize method.

Should I call wait again on my task array in order to properly call Dispose() on each task in the array?

public class MyCancelObject
{
  CancellationTokenSource Source { get;set;}
  int DatabaseId { get;set;}   
}

private void CheckTaskCancelled(object state)
{
  MyCancelObject sourceToken = (MyCancelObject)state;

  if (!sourceToken.CancelToken.IsCancellationRequested)
  {
    //Check database to see if cancelled -- if so, set to cancelled
    sourceToken.CancelToken.Cancel();
  }
}

private void SomeFunc()
{
  Task.StartNew( () =>
  {
    MyCancelObject myCancelObject = new MyCancelObject(
      databaseId,
      new CancellationTokenSource());
    System.Threading.Timer cancelTimer = new Timer(
      new TimerCallback(CheckIfTaskCancelled),
      myCancelObject,
      10000,
      10000);        
    Task[] someTasks = new Task[someNumberOfTasks];

    for (int i = 0; i < someNumberOfTasks; i++)
      someTasks[i] = Task.Factory.StartNew(
        () =>
        {
          DoSomeWork(someObject, myCancelObject.CancelToken.Token);
        },
        TaskCreationOptions.AttachedToParent | TaskCreationOptions.LongRunning,
        myCancelObject.CancelToken.Token);

    try
    {
      Task.WaitAll(someTasks, cts);
    }
    catch (AggregateException)
    {
      //Do stuff to handle
    }
    catch (OperationCanceledException)
    {
      //Should I call Task.WaitAll(someTasks) again??
      //I want to be able to dispose.
    }
  }
}
like image 556
Bryan Crosby Avatar asked Aug 06 '10 18:08

Bryan Crosby


1 Answers

I feel like I have figured this out, but anyone that would like to add anything else useful is more than welcome.

I simply called Task.WaitAll() again from the catch block to wait for the other tasks to finish. After they have all finished, I have a finally block cleaning up all tasks in the array.

try
{
Task.WaitAll(someTaskArray, cancelToken)
}
catch (OperationCanceledException)
{
Task.WaitAll(someTaskArray);
}
finally
{
for (int i = 0; i < someTaskArray.Length; i++)
someTaskArray[i].Dispose();
}
like image 87
Bryan Crosby Avatar answered Sep 29 '22 22:09

Bryan Crosby