У меня есть класс, который запускает задачу и хочет, чтобы задача прекращалась, когда объект был собран мусором.Отмена задачи при завершении объекта
Я реализовал шаблон IDisposable, чтобы гарантировать, что если объект размещен вручную или используется в используемом блоке, задача останавливается правильно. Однако, я не могу гарантировать, что конечный пользователь вызовет Dispose() или использует объект в блоке использования. Я знаю, что сборщик мусора в конечном итоге назовет Finalizer - значит ли это, что задача оставлена?
public class MyClass : IDisposable
{
private readonly CancellationTokenSource feedCancellationTokenSource =
new CancellationTokenSource();
private readonly Task feedTask;
public MyClass()
{
feedTask = Task.Factory.StartNew(() =>
{
while (!feedCancellationTokenSource.IsCancellationRequested)
{
// do finite work
}
});
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
feedCancellationTokenSource.Cancel();
feedTask.Wait();
feedCancellationTokenSource.Dispose();
feedTask.Dispose();
}
}
~MyClass()
{
Dispose(false);
}
}
Был предложен в this question добавить летучее логическое значение, которое устанавливается с Finalizer и наблюдаемое от задачи. Это рекомендуется, или есть лучший способ добиться того, что мне нужно?
(я использую .NET 4, следовательно, использование TaskFactory.StartNew, а не Task.Run)
EDIT:
Чтобы дать некоторый контекст на вопрос - который на самом деле не показанным в приведенном выше фрагменте кода: Я создаю класс сетевого клиента, у которого есть механизм для поддержания регулярности отправки пакетов на сервер. Я решил не приводить все эти детали в примере, поскольку это не имело никакого отношения к моему конкретному вопросу. Однако то, что я действительно хочу, - это возможность для пользователя установить для свойства KeepAlive boolean значение true, которое запустит задачу отправки данных на сервер каждые 60 секунд. Если пользователь устанавливает свойство в false, задача останавливается. IDisposable получил мне 90% пути, однако он полагается на то, что пользователь правильно распоряжается им (явно или с помощью). Я не хочу раскрывать для пользователя задачи сохранения, чтобы они явно меняли, я просто хочу, чтобы «простой» KeepAlive = true/false запускал/останавливал задачу. И я хочу, чтобы задача остановилась, когда пользователь закончил с объект - даже если они не распоряжаются им должным образом. Я начинаю думать, что это невозможно!
Почему бы не использовать 'System.Timers.Timer'? –
Этот код ничего не сделает в финализаторе. Это ошибка. Кроме того, поскольку задача удерживается на внешнем экземпляре MyClass через его состояние закрытия, этот объект никогда не будет завершен до тех пор, пока задача не завершится естественным образом. – usr
@ Danny Chen Я считал это - но имеет ли та же проблема? Насколько я понимаю, таймер будет продолжать работать, даже если объект, создавший его, будет удален (пожалуйста, поправьте меня, если я ошибаюсь)! Я подумал о том, чтобы установить AutoReset в false и сделать метод обратного вызова вручную сбросить его каждый раз, когда он «тикает», - но тогда не было уверенности, что GC когда-либо завершит объект, если он подписался на событие. Мы ценим любые предложения! –