I have viewModelA
and viewA
. I run some time consuming operation in constructor of 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();
}
}
Let's imagine that I run this application which consists just viewA
and viewModelA
and program starts some time-consuming operation(TimeConsumingMethod()
) and suddenly I would like to immediately close the program, but I know that TimeConsumingMethod()
is still running.
So my question is should I cancel a Task inside of finalizer? Or maybe I just should not create a finalizer method cause finalizer should be called for unmanaged resources?
Your proposal certainly seems like an abuse of the finalizer, at first glance. As noted, finalizers are typically for cleaning up unmanaged resources. More importantly, finalizers should not in fact be part of the contractual design of an object. They exist solely for the purpose of acting as a backstop for buggy code, i.e. to clean up resources when the client code has failed to do so explicitly (e.g. by calling IDisposable.Dispose()
).
So the first thing I would look at is how non-buggy code should interact with your class. Is there in fact an IDisposable
implementation? If not, then there also shouldn't be a finalizer. Do you feel strongly you need a finalizer? Then your class should implement IDisposable
(or an equivalent), so that correct code can clean up the object efficiently.
Now, the second thing to look at is whether this task needs to be cancelled at all. What is it that you hope to accomplish by cancelling the task? Do you expect to need to cancel the task in a scenario other than exiting the process? How is the task itself implemented? Do you even start the task anywhere? These are all questions that are not addressed in your question, so there's no way for anyone to address them directly.
I will point out though that, assuming you call the Start()
method at some point, the default implementation for the Task
object is to execute the code using a thread pool thread. Thread pool threads are all background threads, and those threads are killed automatically when all of the foreground threads have exited, allowing the process itself to terminate normally.
So if all you're worried about is the state of the task when the process exits, and you are using the default implementation for the task, and the task can be safely interrupted at any time without corrupting data or leaving some temporary state active (e.g. a temporary file that should be deleted when the task completes), then I don't think you need to cancel the task explicitly.
On the other hand, if there is some reason to cancel the task explicitly, the right way to handle that is to provide a mechanism (e.g. implement IDisposable
or something more explicit) that the client code can use to explicitly notify your object that it is no longer needed and should clean up. When the client code invokes this mechanism, then you can cancel the task.
In this case, you might want to implement a finalizer, but do so knowing that for correctly behaved client code, this finalizer will never be called. It's there only to protect against badly behaved code. It is also very important to understand that there's no guarantee at all that the finalizer will ever be called even for badly behaved code, and the most likely scenario for it to fail to be called is in fact when the process exits.
Finally, if you find you feel a finalizer is in fact required, I urge you to look at the SafeHandle
class. This is a .NET class you can use as a base class for a helper object that will abstract the disposable nature of your task object. In that way, your own object need not implement a finalizer itself; instead, the SafeHandle
subclass you implement will automatically address that need.