You need to write your code so that your task will accept a cancellation token. That's basically just a flag that can be checked by the code in your task, which if updated you would provide logic to handle, having your task's logic safely handle how to terminate its execution, rather than simply stopping at some unknown state. Running the below sample code in LinqPad should give you a reasonable example of what's going on:
void Main()
{
var form = new Form();
var label = new Label(){Text = string.Format("{0:HH:mm:ss}", DateTime.UtcNow), AutoSize = true};
form.Controls.Add(label);
var taskController = new CancellationTokenSource();
var token = taskController.Token;
var task = Task.Run(() =>
{
for (var i=0; i<100; i++)
{
var text = string.Format("{0:HH:mm:ss}", DateTime.UtcNow);
Console.WriteLine(text); //lets us see what the task does after the form's closed
label.Text = text;
if (token.IsCancellationRequested)
{
Console.WriteLine("Cancellation Token Detected");
break;
}
Thread.Sleep(1000);
}
}, token);
form.FormClosed += new FormClosedEventHandler(
(object sender, FormClosedEventArgs e) =>
{taskController.Cancel();}
);
form.Show();
}
Key Points:
Create an instance of CancellationTokenSource
. This is a simple object which will allow you to communicate when you wish to cancel to your task.
var taskController = new CancellationTokenSource();
Fetch the token from this source
var token = taskController.Token;
Run the task, passing a reference to the token
var task = Task.Run(() =>
{
//...
, token
}
Add logic within the task to check the status of this token at suitable points, & handle it appropriately.
if (token.IsCancellationRequested)
{
Console.WriteLine("Cancellation Token Detected");
break;
}
Add logic to call the Cancel
method when you wish to cancel the task. In the above code I've put this under the Form's FormClosed event handler's logic:
taskController.Cancel();
See https://binary-studio.com/2015/10/23/task-cancellation-in-c-and-things-you-should-know-about-it/ for a good write up / related ways to cancel a task.
Side Note
In the above example I was a bit lazy. Each iteration of the loop I check the cancellation token; but then (if not cancelled) wait 1 second before looping. Since the cancel logic only comes into play when the if
statement is evaluated that means that we have to wait 1 second for the cancellation to take effect, which isn't great; if that delay was larger (e.g. 5 minutes), it could be really painful. One solution is outlined here: https://stackoverflow.com/a/17610886/361842
i.e. replace
if (token.IsCancellationRequested)
{
Console.WriteLine("Cancellation Token Detected");
break;
}
Thread.Sleep(1000);
with
if (token.IsCancellationRequested)
{
Console.WriteLine("Cancellation Token Detected");
break;
}
token.WaitHandle.WaitOne(1000);
See https://learn.microsoft.com/en-us/dotnet/api/system.threading.waithandle.waitone?view=netframework-4.7.2 for documentation on the WaitOne
method.