Когда избавляться от System.Threading.Task с дочерними задачами? - PullRequest
11 голосов
/ 06 августа 2010

У меня есть задача, которая запускает несколько дочерних задач.(например, задача A создает B, C, D, E, F).Я также создаю System.Threading.Timer для опроса базы данных каждые 10 секунд, чтобы проверить, был ли запланированный элемент отменен по запросу.Если это так, он устанавливает CancellationTokenSource, чтобы задача знала, что отменить.Каждая подзадача, в данном случае B, C, D, E, F, будет отменена, когда это необходимо (они циклически перебирают файлы и перемещают их).

Поскольку Task реализует IDisposable, Iхотите узнать, будет ли хорошей идеей снова вызвать Task.WaitAll из блока catch, чтобы дождаться отмены.Хотя запрос на отмену будет обработан, подзадачи могут находиться в середине цикла и не могут быть отменены до тех пор, пока он не завершится

Однако для MSDN:

Всегда вызыватьУтилизируйте, прежде чем выпустить последнюю ссылку на задание.В противном случае используемые им ресурсы не будут освобождены до тех пор, пока сборщик мусора не вызовет метод Finalize объекта Task.

Должен ли я снова вызвать wait для моего массива задач, чтобы правильно вызывать Dispose() для каждогозадание в массиве?

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.
    }
  }
}

1 Ответ

2 голосов
/ 23 августа 2010

Мне кажется, я понял это, но любой, кто хотел бы добавить что-нибудь еще полезное, более чем приветствуется.

Я просто снова вызвал Task.WaitAll() из блока catch, чтобы дождаться завершения других задач. После того, как они все закончили, у меня есть наконец блок очистки всех задач в массиве.

try
{
Task.WaitAll(someTaskArray, cancelToken)
}
catch (OperationCanceledException)
{
Task.WaitAll(someTaskArray);
}
finally
{
for (int i = 0; i < someTaskArray.Length; i++)
someTaskArray[i].Dispose();
}
...