是否有必要取消终结器内的任务?

时间:2016-06-29 15:27:16

标签: c# wpf garbage-collection task-parallel-library cancellation

我有viewModelAviewA。我在viewModelA的构造函数中运行了一些耗时的操作:

public class ViewModelA
{
    Task task;
    CancellationTokenSource token;
    public viewModelA()
    {
       task = new Task(TimeConsumingOperation, token.Token);     
    }

    private void TimeConsumingMethod()
    {   
       //some time consuming operation
    }

    ~ViewModelA()
    {
       token.Cancel();
    }
}

让我们假设我运行的应用程序只包含viewAviewModelA,并且程序启动了一些耗时的操作(TimeConsumingMethod())和突然我想立即关闭该计划,但我知道TimeConsumingMethod() 仍在运行。

所以我的问题是我应该在终结器中取消一个任务吗?或者我可能不应该创建一个终结器方法因为应该为非托管资源调用终结器?

1 个答案:

答案 0 :(得分:2)

乍看之下,你的建议似乎是对终结者的滥用。如上所述,终结器通常用于清理非托管资源。更重要的是,终结器实际上不应成为对象合同设计的一部分。它们的存在仅仅是为了充当错误代码的后盾,即在客户端代码未能明确地(例如通过调用IDisposable.Dispose())时清理资源。

所以我要看的第一件事是 -buggy代码应该如何与你的类进行交互。实际上是IDisposable实施吗?如果没有,那么也不应该有终结者。你觉得你需要一个终结者吗?然后你的类应该实现IDisposable(或等效的),以便正确的代码可以有效地清理对象。

现在,要考虑的第二件事是这项任务是否需要取消。你希望通过取消任务来完成什么?您是否希望在场景其他中取消该任务而不是退出该过程?任务本身是如何实现的?你甚至在任何地方开始任务吗?这些都是您问题中未解决的问题,因此任何人都无法直接解决这些问题。

我将指出尽管如此,假设您在某个时刻调用Start()方法,Task对象的默认实现是使用线程池线程执行代码。线程池线程都是后台线程,当所有前台线程都退出时,这些线程会被自动终止,从而允许进程本身正常终止。

因此,如果你担心的是进程退出时的任务状态,你正在使用任务的默认实现,任务可以随时安全地中断,而不会破坏数据或使某些临时状态保持活动状态(例如,任务完成时应删除的临时文件),然后我认为您不需要明确取消任务。

另一方面,如果有某种理由明确取消任务,处理它的正确方法是提供客户端代码可以使用的机制(例如,实现IDisposable或更明确的东西)明确地通知您的对象它不再需要并且应该清理。当客户端代码调用此机制时,您可以取消该任务。

在这种情况下,您可能希望实现终结器,但知道对于正确行为的客户端代码,将永远不会调用此终结器。它只是为了防止表现不佳的代码。同样非常重要的是要理解,即使对于行为不当的代码,也不能保证终结器将被调用,并且最有可能无法调用它的情况实际上是在进程退出时。

最后,如果您发现自己确实需要终结者,我建议您查看SafeHandle课程。这是一个.NET类,可以用作辅助对象的基类,它将抽象出任务对象的一次性特性。这样,你自己的对象就不需要实现终结器了;相反,您实现的SafeHandle子类将自动满足该需求。