-1

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;
        }
    }
}
Amzath
  • 3,159
  • 10
  • 31
  • 43
  • 1
    "It does not look like it cancelled the timer for task.delay." -> Can you elaborate more about what you see versus what you expect? I am not sure what the problem is. – Peter Bons Jan 27 '17 at 12:55
  • I am expecting an exception should be thrown when the task is cancelled. Actually I am seeing task running continuously without aware of the task is cancelled. – Amzath Jan 27 '17 at 13:47

3 Answers3

3

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

Tarek Abo ELkheir
  • 1,311
  • 10
  • 13
0

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;

n.b. 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.
  • If a task is started without being awaited, you won't see exceptions it raised until it is awaited.
Peter Wishart
  • 11,600
  • 1
  • 26
  • 45
0

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>.

smoksnes
  • 10,509
  • 4
  • 49
  • 74