的CancellationToken超时VS task.delay()和超时VS、CancellationToken、delay、task

2023-09-03 13:42:56 作者:檬℃柠叶

我要运行后n毫秒,应该超时的操作。我已经通过传递的CancellationToken集后n毫秒到期实现了它两种途径,一是通过取消操作等待n毫秒后的自己,和一个。

我很担心,因为我的系统进入重负载下的的CancellationToken可能过期操作开始之前。看来,如果我实现了自己使用Task.Delay()的超时,那么延迟()调用不会运行,直到我开始工作后。

下面就是我怎么做的吧:

 公共静态异步任务< T> TimeoutAfter< T>(这个任务< T>的任务,时间跨度超时)
    {
        任务completedTask =等待Task.WhenAny(任务Task.Delay(超时));
        如果(completedTask ==任务)
        {
            返回等待任务;
        }

        抛出新TimeoutException异常();
    }

//使用方法如下
等待SomeOperationAsync()TimeoutAfter(TimeSpan.FromMilliseconds(N))。
 

相比:

  CancellationTokenSource源=新CancellationTokenSource(TimeSpan.FromMilliseconds(N));
等待SomeOperationAsync(source.Token);
 
微软MR技术专家分享 AR VR多线程处理的八年经验与技巧

解决方案

我不知道你的担心是有道理的,但我会认为它是。

这是使用你的code问题 Task.Delay()是,你没有真正取消操作。这可能意味着你是在浪费资源,或misinforming你的用户(你告诉他们,操作超时,而操作仍在运行,将最有可能成功完成)。

现在,如果你想确保取消标记启动开始运转后,才越来越少,那么的做的:

 无功源=新CancellationTokenSource();
VAR任务= SomeOperationAsync(source.Token);
source.CancelAfter(TimeSpan.FromMilliseconds(正));
等待任务;
 

如果你经常这样做,你可能想封装这一逻辑到一个方法(可能需要一个更好的名字):

 公共静态异步任务WithTimeoutAfterStart(
    FUNC<的CancellationToken,任务>操作,时间跨度超时)
{
    无功源=新CancellationTokenSource();
    VAR任务=操作(source.Token);
    source.CancelAfter(超时);
    等待任务;
}
 

用法:

 等待WithTimeoutAfterStart(
    克拉=> SomeOperationAsync(CT),TimeSpan.FromMilliseconds(正));
 

I want to run an operation that should timeout after n milliseconds. I've implemented it two ways, one by cancelling the operation myself after waiting n milliseconds, and one by passing in a CancellationToken set to expire after n milliseconds.

I'm worried that as my system goes under heavy load, the cancellationtoken could expire before the operation even starts. It seems that if I implement the timeout myself using Task.Delay(), then the Delay() call won't run until after my operation begins.

Here's how I'm doing it:

public static async Task<T> TimeoutAfter<T>(this Task<T> task, TimeSpan timeout)
    {
        Task completedTask = await Task.WhenAny(task, Task.Delay(timeout));
        if (completedTask == task)
        {
            return await task;
        }

        throw new TimeoutException();
    }

// Use it like this
await SomeOperationAsync().TimeoutAfter(TimeSpan.FromMilliseconds(n));

Compared to:

CancellationTokenSource source = new CancellationTokenSource(TimeSpan.FromMilliseconds(n));
await SomeOperationAsync(source.Token);

解决方案

I'm not sure that your worry is justified, but I'm going to assume that it is.

The problem with your code that uses Task.Delay() is that you're not actually canceling the operation. This might mean that you're wasting resources or misinforming your users (you tell them that the operation timed out, while the operation is still running and will most likely complete successfully).

Now, if you want to make sure that the cancellation token starts ticking only after the operation is started, then do that:

var source = new CancellationTokenSource();
var task = SomeOperationAsync(source.Token);
source.CancelAfter(TimeSpan.FromMilliseconds(n));
await task;

If you do this often, you might want to encapsulate this logic into a method (might need a better name):

public static async Task WithTimeoutAfterStart(
    Func<CancellationToken, Task> operation, TimeSpan timeout)
{
    var source = new CancellationTokenSource();
    var task = operation(source.Token);
    source.CancelAfter(timeout);
    await task;
}

Usage:

await WithTimeoutAfterStart(
    ct => SomeOperationAsync(ct), TimeSpan.FromMilliseconds(n));