【问题标题】:cancellationtoken timeout vs task.delay() and timeoutcanceltoken 超时与 task.delay() 和超时
【发布时间】:2014-06-21 23:55:45
【问题描述】:

我想运行一个应该在 n 毫秒后超时的操作。我有两种实现方式,一种是在等待 n 毫秒后自己取消操作,另一种是传入设置为在 n 毫秒后过期的 CancellationToken。

我担心当我的系统负载过重时,取消令牌可能会在操作开始之前过期。看来,如果我自己使用 Task.Delay() 实现超时,那么在我的操作开始之前,Delay() 调用将不会运行。

这是我的做法:

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));

相比:

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

【问题讨论】:

  • 在操作开始之前使 CancellationTokenSource 过期是一项繁重的工作。

标签: .net task-parallel-library


【解决方案1】:

我不确定你的担心是否合理,但我会假设它是正确的。

使用Task.Delay() 的代码的问题在于您实际上并没有取消操作。这可能意味着您在浪费资源或误导用户(您告诉他们操作已超时,而操作仍在运行并且很可能会成功完成)。

现在,如果您想确保取消令牌仅在操作开始后才开始计时,那么执行此操作

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

如果您经常这样做,您可能希望将此逻辑封装到一个方法中(可能需要一个更好的名称):

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;
}

用法:

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

【讨论】:

  • CancellationTokenSource source 不应该在using 块中吗?
猜你喜欢
  • 2013-04-29
  • 2012-12-01
  • 1970-01-01
  • 1970-01-01
  • 2017-07-02
  • 2019-07-25
  • 2018-07-03
  • 1970-01-01
  • 2012-12-19
相关资源
最近更新 更多