3

问题:有没有办法将 aCancellationToken与方法Task返回的值关联起来async

通常,如果 a与匹配的's一起抛出, aTask将最终进入Canceled状态。如果它们不匹配,则任务进入状态:OperationCancelledExceptionCancellationTokenTaskCancellationTokenFaulted

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,我还没有找到设置方法的方法TaskCancellationToken从而实现相同的功能)。从我的测试来看,似乎任何 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方法被取消(并且我不希望取消 - 即它不是 thisTaskCancellationToken),我希望任务进入Faulted状态- -不是Canceled国家。

4

2 回答 2

2

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

如果你想要不同的语义,你可以catchOperationCanceledException你的async方法中抛出一个符合你需要的语义的异常。如果你想重复使用这些语义,一个扩展方法Task应该适合。

于 2013-03-06T20:39:43.880 回答
0

这是一个在提供参数时Run试图模仿该方法行为的方法。仅当 async 方法返回一个任务并且关联的匹配提供的参数时,才能返回的任务。Task.RunCancellationTokenRunCanceledCanceledCancellationToken

/// <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)
        {
            try { t.GetAwaiter().GetResult(); }
            catch (Exception ex)
            {
                // In case the async method is canceled with an unknown token, throw
                // the exception. The continuation will complete in a Faulted state.
                if (ex is OperationCanceledException oce &&
                    oce.CancellationToken != cancellationToken) throw;
            }
        }

        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
于 2021-10-12T08:59:30.270 回答