将 CancellationToken 与异步方法的任务相关联

问题描述 投票:0回答:2

问题:有没有办法将

CancellationToken
与从
Task
方法返回的
async
相关联?

通常,如果

Task
与与
Canceled
OperationCancelledException
匹配的
CancellationToken
一起抛出,
Task
最终会处于
CancellationToken
状态。如果它们不匹配,则任务进入
Faulted
状态:

void WrongCancellationTokenCausesFault()
{
    var cts1 = new CancellationTokenSource();
    var cts2 = new CancellationTokenSource();
    cts2.Cancel();

    // This task will end up in the Faulted state due to the task's CancellationToken
    // not matching the thrown OperationCanceledException's token.
    var task = Task.Run(() => cts2.Token.ThrowIfCancellationRequested(), cts1.Token);  
}

对于

async
/
await
,我还没有找到一种方法来设置方法的
Task
CancellationToken
(从而实现相同的功能)。从我的测试来看,似乎 any
OperationCancelledException
会导致
async
方法进入 Canceled 状态:

async Task AsyncMethodWithCancellation(CancellationToken ct)
{
    // If ct is cancelled, this will cause the returned Task to be in the Cancelled state
    ct.ThrowIfCancellationRequested(); 
    await Task.Delay(1);

    // This will cause the returned Task to be in the Cancelled state
    var newCts = new CancellationTokenSource();
    newCts.Cancel();
    newCts.Token.ThrowIfCancellationRequested();
}

如果能有更多的控制权就好了,因为如果我从

async
方法调用的方法被取消(而且我不希望取消——即它不是这个
Task
CancellationToken
),我希望任务进入
Faulted
状态,而不是
Canceled
状态。

c# async-await task-parallel-library cancellation-token
2个回答
2
投票

我认为该设计非常适合常见情况:如果取消任何子操作,则取消会传播到父级(最常见的情况是父级和子级共享取消令牌)。

如果您想要不同的语义,您可以在

catch
方法中
OperationCanceledException
async
并抛出符合您需要的语义的异常。如果您想重复使用这些语义,
Task
的扩展方法应该符合要求。


0
投票

这是一个

Run
方法,当提供
Task.Run
参数时,它尝试模仿
CancellationToken
方法的行为。如果异步方法返回
Run
任务,并且关联的 Canceled
 与提供的参数匹配,则 
Canceled
返回的任务只能变为
CancellationToken

/// <summary>
/// Invokes an async method (a method implemented with the async keyword), and
/// returns a proxy of the produced async task. In case the async task completes
/// in a Canceled state but the causative CancellationToken is not equal with the
/// cancellationToken argument, the proxy transitions to a Faulted state.
/// In all other cases, the proxy propagates the status of the async task as is.
/// </summary>
public static Task<TResult> Run<TResult>(Func<Task<TResult>> asyncMethod,
    CancellationToken cancellationToken)
{
    return asyncMethod().ContinueWith(t =>
    {
        if (t.IsCanceled)
        {
            // In case the async method is canceled with an unknown token, throw
            // an exception. The continuation will complete in a Faulted state.
            CancellationToken taskToken = new TaskCanceledException(t).CancellationToken;
            if (taskToken != cancellationToken)
                return Task.FromException<TResult>(new OperationCanceledException(taskToken));
        }

        return t; // In any other case, propagate the task as is.
    }, default, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default)
    .Unwrap();
}

使用示例:

Task<int> task = Run(async () =>
{
    await Task.Delay(1000, new CancellationToken(true));
    return 13;
}, new CancellationToken(false));

try { task.Wait(); } catch { }

Console.WriteLine(task.Status);

输出:

Faulted
© www.soinside.com 2019 - 2024. All rights reserved.