【问题标题】:Wait For Task But Forget It After Certain Time [duplicate]等待任务但在一段时间后忘记它[重复]
【发布时间】:2019-06-11 23:13:22
【问题描述】:

有没有办法等待任务结束但在一段时间后继续执行但让任务本身继续执行?我希望在 5 秒内从任务中得到响应,并且不想等待更长的时间 - 但是,如果超过 5 秒,我只想继续前进而不是实际取消任务(排除取消令牌)。

我正在使用 .NET Core / C#。

private async Task<string> MakeAuthRequest(Request request)
{
    try
    {
        var response = await SometimesLongMethod(request); // something needs to be done here, just not sure what
        return response.Message;
    }
    catch(Exception e)
    {
        logger.Error(e, "Error occurred");
        return string.Empty;
    }
}

【问题讨论】:

  • Task.Delay + Task.WaitAny?
  • 那么如果您停止等待并让任务在后台继续执行,您的回报会怎样?或许返回default(string)
  • 我迷路了,如果任务还没有结束,你打算怎么回东西?
  • Task.Wait(TimeSpan)Task.Wait(Int32) 都允许您指定等待的最长时间。

标签: c# asp.net-core task wait


【解决方案1】:

您可以使用Task.Delay 让一个任务代表一个时间跨度,然后使用Task.WhenAny 确定哪个任务首先完成:

private async Task<string> MakeAuthRequest(Request request)
{
  try
  {
    var timer = Task.Delay(TimeSpan.FromSeconds(5));
    var authTask = SometimesLongMethod(request);
    var completedTask = await Task.WhenAny(timer, authTask);
    if (completedTask == authTask)
      return await authTask;
    return string.Empty; // Did not complete in 5 seconds.
  }
  catch(Exception e)
  {
    logger.Error(e, "Error occurred");
    return string.Empty;
  }
}

【讨论】:

  • 如果您的任务返回,您应该始终取消延迟任务。或者你会得到悬空的计时器。
  • 我认为即使满足超时条件,OP 也希望 MakeAuthRequest 任务继续执行,您上面的 sn-p 不会这样做它需要成为任务链的下一个级别。跨度>
  • @Jamiec:延迟任务稍后才会完成。虽然您可以取消它,但我认为它不会产生太大影响。
  • @Digitalsa1nt:我将 OP 的请求解释为想要在他们有 // something needs to be done here 占位符的地方进行超时。
【解决方案2】:

这样就可以了——如果你的方法快速返回,记得取消延迟任务!

 public static async Task<T> OnlyWaitFor<T>(Task<T> theTask, TimeSpan waitTime)
 {
    var cts = new CancellationTokenSource();
    var delayTask = Task.Delay(waitTime,cts.Token);
    var result = await Task.WhenAny(delayTask, theTask);
    if (result == delayTask)
        return default(T);
    cts.Cancel();
    return await theTask;

}

测试代码:

 public static async Task<string> ShortTask()
 {
     await Task.Delay(500);
     return "ShortFoo";
 }

 public static async Task<string> LongTask()
 {
     await Task.Delay(5000);
     return "LongFoo";
 }

var sw = new Stopwatch();
sw.Start();
var result1 = await OnlyWaitFor(ShortTask(), TimeSpan.FromSeconds(1));
sw.Stop();
Console.WriteLine($"Got back '{result1}' in {sw.ElapsedMilliseconds}ms");

sw.Reset();
sw.Start();
var result2 = await OnlyWaitFor(LongTask(), TimeSpan.FromSeconds(1));
sw.Stop();
Console.WriteLine($"Got back '{result2}' in {sw.ElapsedMilliseconds}ms");

输出:

Got back 'ShortFoo' in 524ms
Got back '' in 1006ms

【讨论】:

    【解决方案3】:

    下面的扩展方法允许为等待任何任务设置超时。任务本身将继续运行。超时Task&lt;T&gt; 的结果将是T 的默认值。例如,超时的Task&lt;int&gt; 结果为 0。

    public static Task WithTimeout(this Task task, int timeout)
    {
        var delayTask = Task.Delay(timeout);
        return Task.WhenAny(task, delayTask).Unwrap();
    }
    
    public static Task<T> WithTimeout<T>(this Task<T> task, int timeout)
    {
        var delayTask = Task.Delay(timeout).ContinueWith(_ => default(T),
            TaskContinuationOptions.ExecuteSynchronously);
        return Task.WhenAny(task, delayTask).Unwrap();
    }
    

    使用示例可以在this answer中找到。

    如果T 的默认值是有效的返回值,您可能更愿意处理异常:

    public static Task<T> WithTimeoutException<T>(this Task<T> task, int timeout)
    {
        var delayTask = Task.Delay(timeout).ContinueWith<T>(_ => throw new TimeoutException(),
            TaskContinuationOptions.ExecuteSynchronously);
        return Task.WhenAny(task, delayTask).Unwrap();
    }
    

    更新:如果您改变主意并希望取消超时任务,请参阅此问题:Task.WhenAny with cancellation of the non completed tasks and timeout

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-08-06
      • 2018-03-20
      • 1970-01-01
      • 2016-10-27
      • 1970-01-01
      • 2016-06-18
      • 2022-11-15
      • 1970-01-01
      相关资源
      最近更新 更多