【发布时间】:2016-04-06 09:48:41
【问题描述】:
我有一个启动任务的类,并希望确保在对象被垃圾收集时任务停止。
我已经实现了 IDisposable 模式,以确保如果手动释放对象或在 using 块中使用对象,则任务会正确停止。 但是,我不能保证最终用户会调用 Dispose() 或在 using 块中使用该对象。我知道垃圾收集器最终会调用终结器——这是否意味着任务仍在运行?
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 中建议添加一个 volatile bool,它是从 Finalizer 设置并从任务中观察到的。这是推荐的,还是有更好的方法来实现我的需要?
(我使用的是 .NET 4,因此使用 TaskFactory.StartNew 而不是 Task.Run)
编辑:
为了给这个问题提供一些上下文 - 这实际上并没有在上面的代码 sn-p 中显示:我正在创建一个网络客户端类,它具有通过定期向服务器发送数据包来保持活动状态的机制。我选择不将所有这些细节都放在示例中,因为它与我的具体问题无关。但是,我真正想要的是用户能够将 KeepAlive 布尔属性设置为 true,这将启动每 60 秒向服务器发送数据的任务。如果用户将该属性设置为 false,则任务将停止。 IDisposable 让我完成了 90% 的工作,但它依赖于用户正确处理它(明确地或通过使用)。我不想向用户公开保持活动任务让他们明确取消,我只想要一个“简单”的 KeepAlive = true/false 来启动/停止任务,并且我希望任务在用户完成时停止对象 - 即使他们没有正确处理它。我开始认为这是不可能的!
【问题讨论】:
-
为什么不使用
System.Timers.Timer? -
这段代码在终结器中不会做任何事情。那是一个错误。此外,由于任务通过其关闭状态保持在其外部 MyClass 实例上,因此该对象将永远不会被最终确定,直到任务自然退出。
-
@Danny Chen 我考虑过 - 但这有同样的问题吗?据我了解,即使创建它的对象已被处置,计时器也会继续运行(如果我错了,请纠正我)!我曾考虑将 AutoReset 设置为 false 并让回调方法在每次“滴答”时手动重置它 - 但随后不确定如果 GC 订阅了事件,它是否会最终确定对象。任何建议将不胜感激!
-
Dispose() 是一把锤子,它可以敲打太多钉子。这里只是大错特错终结器没用,任务正在使用对象。调用 Dispose() 只是在尝试使用已处理的对象时使任务随机失败的好方法。取消任务不是即时的。你必须停止使用 Dispose,它的合约根本无法完成工作。
-
@phil_rawlings 为什么要存储任务?为什么将其包装在自定义类中?如果要取消任务,请传递从用户控制的 CTS 创建的 CancellationToken。用户应直接调用
Cancel(),而不是通过阻塞 Dispose 间接调用
标签: c# task-parallel-library task idisposable finalizer