Reputation: 16280
I have a method that loads data, eg.:
public async GetCustomers()
{
await Task.Run(() => {
for (int i = 0; i < 99; i++)
customers = customerRequest.GetCustomers();
});
}
customerRequest
is a simple class that uses HttpClient
to connect to a WebApi server. The call stack is as follows:
method->request->controller action->server layer->repository->db
Currently, the controller action returns IHttpActionResult
, whilst the service and repository layers return IEnumerable<Customer>
. All methods are called synchronously.
For testing purposes, I added the for
loop to increase the task's delay and see the SQL statements being executed.
If the user decides to close the form, the task is still being executed in the background and the SQL statements are still sent to the db.
What is the correct way to cancel such a task?
Upvotes: 1
Views: 3588
Reputation: 456322
You'd want to use CancellationTokenSource
, and pass its CancellationToken
into your method. When the form closes, call CancellationTokenSource.Cancel
.
Note, however, that you want to pass the CancellationToken
into the methods that actually use it. As I describe on my blog, Task.Run
is not one of them.
What you really want to do is start at the lowest level (on the client side), and pass the CancellationToken
into whatever HttpClient
method that you're using (i.e., like this one).
Then work your way up. I'd also recommend making your code asynchronous. When you're done, you should end up with a GetCustomers
that looks like this:
public async Task GetCustomersAsync(CancellationToken token)
{
for (int i = 0; i < 99; i++)
customers = await customerRequest.GetCustomersAsync(token);
}
If you want to Really Be Sure (tm) that no spurious requests go out, you can also explicitly check the token before doing the request:
public async Task GetCustomersAsync(CancellationToken token)
{
for (int i = 0; i < 99; i++)
{
token.ThrowIfCancellationRequested();
customers = await customerRequest.GetCustomersAsync(token);
}
}
You can also handle cancellation on the server side if that's important to you.
Upvotes: 3
Reputation: 1893
Checkout CancellationTokenSource. You can keep one of these long lived and a button or a close event call Cancel() on it. It just needs to be passed to all your tasks if you want them all to cancel. The task will throw an exception so make sure you wrap in a try catch. You can also check if a cancel request has been made within your task to try to break out in a graceful manor.
var cts = new CancellationTokenSource();
await Task.Run(() =>
{
//do work, will throw if cts.Cancel() is called
}, cts.Token);
//wait 2 seconds then cancel
await Task.Delay(2000);
cts.Cancel();
Upvotes: 1