【问题标题】:Cancelling a Long Running Task Delay取消长时间运行的任务延迟
【发布时间】:2017-01-27 11:36:19
【问题描述】:

我正在尝试取消由 Web api 请求创建的异步延迟任务 (Task.Delay),并使用另一个发出取消的 Web api 请求。看起来它没有取消 task.delay 的计时器。这是我正在尝试实现的代码 sn-p。为了您的信息,我使用 Application 对象来存储 CancellationTokenSource 对象以跨多个请求检索令牌源。 更新 问题是我希望通过从代码中抛出异常来取消任务。但它从未发生过。如何使此代码取消 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;
        }
    }
}

【问题讨论】:

  • "它似乎没有取消 task.delay 的计时器。" -> 你能详细说明你看到的和你期望的吗?我不确定是什么问题。
  • 我希望在取消任务时会引发异常。实际上我看到任务连续运行而没有意识到任务被取消。

标签: c# asp.net-web-api async-await task cancellationtokensource


【解决方案1】:

我认为它被取消了,您可以通过添加 try catch 来尝试:

 try
  {
     await Task.Delay(5000, token);
   }
  catch(TaskCanceledException ex)
    {

    }

你会看到它进入了catch块,该方法没有返回任何东西,因为TaskCanceledException

【讨论】:

  • 我用控制台应用程序测试了上面的代码,它工作正常
【解决方案2】:

你的代码看起来是正确的,我是这样测试的:

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

  • 在您创建 CancellationTokenSource 之后,token.ThrowIfCancellationRequested() 什么也不做 - 如果某些代码已经取消了源,则此方法实际上只是引发异常。
  • 如果任务在没有等待的情况下启动,在等待之前您不会看到它引发的异常。

【讨论】:

    【解决方案3】:

    尝试使用TaskCompletionSource。它对我有用,但我不确定这是否是你要找的。​​p>

    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";
    

    另外,没有必要保留Cancel async。你可以返回一个字符串而不是Task&lt;string&gt;

    【讨论】:

      猜你喜欢
      • 2019-02-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-10-25
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多