Asynchronously wait for Task to complete with timeout

后端 未结 16 2006
天命终不由人
天命终不由人 2020-11-21 17:49

I want to wait for a Task to complete with some special rules: If it hasn\'t completed after X milliseconds, I want to display a message to the user. And

16条回答
  •  迷失自我
    2020-11-21 18:39

    A few variants of Andrew Arnott's answer:

    1. If you want to wait for an existing task and find out whether it completed or timed out, but don't want to cancel it if the timeout occurs:

      public static async Task TimedOutAsync(this Task task, int timeoutMilliseconds)
      {
          if (timeoutMilliseconds < 0 || (timeoutMilliseconds > 0 && timeoutMilliseconds < 100)) { throw new ArgumentOutOfRangeException(); }
      
          if (timeoutMilliseconds == 0) {
              return !task.IsCompleted; // timed out if not completed
          }
          var cts = new CancellationTokenSource();
          if (await Task.WhenAny( task, Task.Delay(timeoutMilliseconds, cts.Token)) == task) {
              cts.Cancel(); // task completed, get rid of timer
              await task; // test for exceptions or task cancellation
              return false; // did not timeout
          } else {
              return true; // did timeout
          }
      }
      
    2. If you want to start a work task and cancel the work if the timeout occurs:

      public static async Task CancelAfterAsync( this Func> actionAsync, int timeoutMilliseconds)
      {
          if (timeoutMilliseconds < 0 || (timeoutMilliseconds > 0 && timeoutMilliseconds < 100)) { throw new ArgumentOutOfRangeException(); }
      
          var taskCts = new CancellationTokenSource();
          var timerCts = new CancellationTokenSource();
          Task task = actionAsync(taskCts.Token);
          if (await Task.WhenAny(task, Task.Delay(timeoutMilliseconds, timerCts.Token)) == task) {
              timerCts.Cancel(); // task completed, get rid of timer
          } else {
              taskCts.Cancel(); // timer completed, get rid of task
          }
          return await task; // test for exceptions or task cancellation
      }
      
    3. If you have a task already created that you want to cancel if a timeout occurs:

      public static async Task CancelAfterAsync(this Task task, int timeoutMilliseconds, CancellationTokenSource taskCts)
      {
          if (timeoutMilliseconds < 0 || (timeoutMilliseconds > 0 && timeoutMilliseconds < 100)) { throw new ArgumentOutOfRangeException(); }
      
          var timerCts = new CancellationTokenSource();
          if (await Task.WhenAny(task, Task.Delay(timeoutMilliseconds, timerCts.Token)) == task) {
              timerCts.Cancel(); // task completed, get rid of timer
          } else {
              taskCts.Cancel(); // timer completed, get rid of task
          }
          return await task; // test for exceptions or task cancellation
      }
      

    Another comment, these versions will cancel the timer if the timeout does not occur, so multiple calls will not cause timers to pile up.

    sjb

提交回复
热议问题