How can code within a Task know that it has timed

2019-05-31 22:24发布

问题:

Consider the following code:

public class EventManager
{
  public Task<string> GetResponseAsync(string request)
  {
    CancellationTokenSource tokenSource = new CancellationTokenSource();

    return new Task<string>( () =>
    {
      // send the request
      this.Send(request);

      // wait for the response until I've been cancelled or I timed out.
      // this is important because I want to cancel my "wait" if either occur

      // WHAT CODE CAN I WRITE HERE TO SEE IF THIS TASK HAS TIMED OUT?
      // (see the example below)
      // 
      // Note that I'm not talking about cancellation
      // (tokenSource.Token.IsCancellationRequested)

      return response;
    }, tokenSource.Token);
  }
}


public static void Main()
{
  EventManager mgr = new EventManager();
  Task<string> responseTask = mgr.GetResponseAsync("ping");
  responseTask.Start();

  if (responseTask.Wait(2000))
  {
    Console.WriteLine("Got response: " + responseTask.Result);
  }
  else
  {
    Console.WriteLine("Didn't get a response in time");
  }
}

回答1:

You can't know if your Task timed out because it actually never times out in this sample. The Wait API will block on the Task completing or the specified time ellapsing. If the time ellapses nothing happens to the Task itself, the caller of Wait simply returns with false. The Task continues to run unchanged

If you want to communicate to the Task that you are no longer interested in it's results the best way is to use cancellation.



回答2:

Task does not inlcude timeout functionality out of the box. You can add that essentially by starting a Timer that will cancel the task after the timeout, if it hasn't already completed.

Joe Hoad provides an implementation at the Parallel FX Team blog that does this and covers several edge cases that one could easily overlook.



回答3:

In this case, you won't.

If you want to be able to kill off a task that doesn't return in time, you need to pass in your cancellation token to the async call (vs. creating it inside that method) so you can signal for it to cancel from your caller (Main in this case).