在Task.Run中使用CancellationToken超时不起作用

时间:2014-03-25 14:32:12

标签: c# .net task-parallel-library cancellationtokensource cancellation-token

好的,我的问题非常简单。为什么这段代码不会抛出TaskCancelledException

static void Main()
{
    var v = Task.Run(() =>
    {
        Thread.Sleep(1000);
        return 10;
    }, new CancellationTokenSource(500).Token).Result;

    Console.WriteLine(v); // this outputs 10 - instead of throwing error.
    Console.Read();
}

但是这个有效

static void Main()
{
    var v = Task.Run(() =>
    {
        Thread.Sleep(1000);
        return 10;
    }, new CancellationToken(true).Token).Result;

    Console.WriteLine(v); // this one throws
    Console.Read();
}

4 个答案:

答案 0 :(得分:32)

Cancellation in Managed Threads

  

取消是合作的,不会强制听众。侦听器确定如何优雅地终止以响应取消请求。

您没有在Task.Run方法中编写任何代码来访问CancellationToken并实施取消 - 因此您实际上忽略了取消请求并且已完成。

答案 1 :(得分:18)

取消正在运行的任务和计划运行的任务有所不同。

在调用Task.Run方法之后,该任务仅被安排,可能尚未执行。

当您使用具有取消支持的Task.Run(...,CancellationToken)重载系列时,将在任务即将运行时检查取消令牌。如果取消令牌此时将IsCancellationRequested设置为true,则抛出TaskCanceledException类型的异常。

如果任务已在运行,则任务负责调用ThrowIfCancellationRequested方法,或者只是抛出OperationCanceledException。

根据MSDN,它只是以下方便的方法:

  

if(token.IsCancellationRequested)       抛出新的OperationCanceledException(token);

在这两种情况下不使用不同类型的异常:

catch (TaskCanceledException ex)
{
    // Task was canceled before running.
}
catch (OperationCanceledException ex)
{
    // Task was canceled while running.
}

另请注意,TaskCanceledException来自OperationCanceledException,因此您只能为catch类型设置一个OperationCanceledException子句:

catch (OperationCanceledException ex)
{
    if (ex is TaskCanceledException)
        // Task was canceled before running.
    // Task was canceled while running.
}

答案 2 :(得分:16)

我认为因为你没有从CancellationToken对象调用方法ThrowIfCancellationRequested()。 以这种方式,您忽略了取消任务的请求。

你应该这样做:

void Main()
{
    var ct = new CancellationTokenSource(500).Token;
     var v = 
     Task.Run(() =>
    {
        Thread.Sleep(1000);
        ct.ThrowIfCancellationRequested();
        return 10;
    }, ct).Result;

    Console.WriteLine(v); //now a TaskCanceledException is thrown.
    Console.Read();
}

代码的第二个变体有效,因为您已经初始化了Canceled状态设置为true的标记。实际上,正如所述here

If canceled is true, both CanBeCanceled and IsCancellationRequested will be true

已经请求取消,然后会立即抛出异常TaskCanceledException,而不会实际启动任务。

答案 3 :(得分:1)

另一个使用Task.Delay和令牌的实现,而不是Thread.Sleep。

 static void Main(string[] args)
    {
        var task = GetValueWithTimeout(1000);
        Console.WriteLine(task.Result);
        Console.ReadLine();
    }

    static async Task<int> GetValueWithTimeout(int milliseconds)
    {
        CancellationTokenSource cts = new CancellationTokenSource();
        CancellationToken token = cts.Token;
        cts.CancelAfter(milliseconds);
        token.ThrowIfCancellationRequested();

        var workerTask = Task.Run(async () =>
        {
            await Task.Delay(3500, token);
            return 10;
        }, token);

        try
        {
            return await workerTask;
        }
        catch (OperationCanceledException )
        {
            return 0;
        }
    }