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

Is it necessary to cancel Task inside of finalizer?

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

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()仍然运行宁。

所以我的问题是我应该取消终结器内部的任务吗? 或者也许我不应该创建终结器方法因为应该为非托管资源调用终结器?

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

所以我首先要看的是错误代码应该如何与您的class交互。实际上有 IDisposable 实现吗?如果不是,那么也不应该有终结器。你强烈觉得你需要一个终结器吗?然后你的 class 应该实现 IDisposable (或等效的),以便正确的代码可以有效地清理对象。

现在,第二个要看的是这个任务是否需要取消。您希望通过取消任务来完成什么?您是否希望在 other 场景中取消任务而不是退出流程?任务本身是如何实现的?你甚至在任何地方开始任务吗?这些都是您的问题中没有涉及的问题,因此任何人都无法直接解决这些问题。

我要指出的是,假设您在某个时候调用了 Start() 方法,Task 对象的默认实现是使用线程池线程执行代码。线程池线程都是后台线程,当所有前台线程都退出时,这些线程会自动终止,让进程本身正常终止。

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

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

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

最后,如果您觉得确实需要终结器,我建议您查看 SafeHandle class。这是一个 .NET class,您可以将其用作辅助对象的基础 class,该辅助对象将抽象出任务对象的一次性性质。这样,您自己的对象就不需要自己实现终结器;相反,您实施的 SafeHandle subclass 将自动满足该需求。