取消等待自己

Cancel awaiting itself

我有以下示例代码:

public static async Task Async()
{
    CancellationTokenSource source = new CancellationTokenSource();
    source.CancelAfter(500);
    Stopwatch sw = Stopwatch.StartNew();
    await RunThread(ExpensiveOperation, source.Token);
    sw.Stop();
    Console.WriteLine(sw.Elapsed);
}

public static async Task RunThread(Action act, CancellationToken token)
{   //modify this method to handle cancelling the token during the following await
    await Task.Run(act); //Task.Run(act, token) doesn't help
}

public static void ExpensiveOperation()
{
    Thread.Sleep(1000); //simulates CPU expensive operation
}

现在,我如何修改 RunThread 方法以实际停止等待长任务,方法是注册被取消的任务,因此 return 在 500 毫秒之后,而不是等待实际完成ExpensiveOperation?

您应该将令牌传递给操作本身,并时常检查它:

public static async Task RunThread(Action<CancellationToken> act, CancellationToken token)
{   
    await Task.Run(() => act(token), token);
}

public static void ExpensiveOperation(CancellationToken token)
{
    for (int i = 0; i < 10; i++)
    {
        token.ThrowIfCancellationRequested();
        Thread.Sleep(100);
    }
}

您还将令牌传递给 Task.Run,这样返回的 Task 就会知道它已被取消,而不仅仅是出错。

如果您无法从 ExpensiveOperation 内部取消(您无法更改代码,或者它实际上是异步操作而不是同步操作),则使用 WithCancellation 扩展方法:

static Task WithCancellation(this Task task, CancellationToken cancellationToken)
{
    return task.IsCompleted
        ? task
        : task.ContinueWith(
            completedTask => completedTask.GetAwaiter().GetResult(),
            cancellationToken,
            TaskContinuationOptions.ExecuteSynchronously,
            TaskScheduler.Default);
}

public static async Task RunThread(Action act, CancellationToken token)
{
    await Task.Run(act).WithCancellation(token);
}

请注意,这种方法实际上并没有取消操作,它只是让您的代码流表现得像取消操作一样。