I am trying to cancel a async delay task (Task.Delay) that was created by a web api request, with an another web api request that issues cancel. It does not look like it cancelled the timer for task.delay. Here is the code snippet that I am trying to implement. For your information I am using Application object to store the CancellationTokenSource object to retrieve the token source across multiple requests. Update Question is I am expecting the task to be cancelled by throwing an exception from the code. But it never happened. How do I make this code to cancel the task.delay?
using Microsoft.Practices.Unity;
using System;
using System.Collections.Concurrent;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Web;
using System.Web.Http;
namespace WebApplication8.Controllers
{
public class TaskController : ApiController
{
[HttpGet]
public async Task<string> CreateTask(int id)
{
var tokenSource = new CancellationTokenSource();
var concurrentTokens = GetConcurrentTokens();
concurrentTokens.TryAdd(id, tokenSource);
CancellationToken token = tokenSource.Token;
token.ThrowIfCancellationRequested();
await Task.Delay(50000,token);
return "Task Created";
}
[HttpGet]
public async Task<string> Cancel(int id)
{
var concurrentTokens = GetConcurrentTokens();
CancellationTokenSource item = concurrentTokens.First(t => t.Key == id).Value;
item.Cancel();
item.Dispose();
var tokenSource2 = new CancellationTokenSource();
concurrentTokens.TryRemove(id,out tokenSource2);
return "Cancelled";
}
private ConcurrentDictionary<long, CancellationTokenSource> GetConcurrentTokens()
{
var tokens = HttpContext.Current.Application["Tokens"];
if (tokens == null)
{
tokens = new ConcurrentDictionary<long, CancellationTokenSource>();
HttpContext.Current.Application["Tokens"] = tokens;
}
return (ConcurrentDictionary<long, CancellationTokenSource>) tokens;
}
}
}
I think it get cancelled , you can try it by adding try catch like this:
try
{
await Task.Delay(5000, token);
}
catch(TaskCanceledException ex)
{
}
And you will see it enters the catch block, the method doesn't return any thing because TaskCanceledException
Your code looks correct, I tested it like this:
var tc = new TaskController();
var backTask1 = tc.CreateTask(1);
var backTask2 = tc.CreateTask(2);
// task 2 gets cancelled
await tc.Cancel(2);
try
{
var res2 = await backTask2;
}
catch (OperationCanceledException) { }
// task 1 waits
await backTask1;
nb that:
token.ThrowIfCancellationRequested()
does nothing just after you created the CancellationTokenSource
- this method literally just throws an exception if some code has cancelled the source already. Try using TaskCompletionSource
. It works for me, but I'm not sure if this is what you are looking for.
var source = new CancellationTokenSource();
var concurrentTokens = GetConcurrentTokens();
concurrentTokens.TryAdd(id, source);
var completionSource = new TaskCompletionSource<object>();
source.Token.Register(() => completionSource.TrySetCanceled());
var task = Task.Delay(50000, source.Token);
// Continue when any of these are done.
await Task.WhenAny(task, completionSource.Task);
if (task.IsCanceled)
{
return "Task was not created";
}
return "Task Created";
Also, there's no need to keep Cancel
async
. You can return a string instead of Task<string>
.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.