如何在复杂的异步等待场景中实现超时和取消[重复]

问题描述 投票:-2回答:2

这个问题在这里已有答案:

我对如何为以下代码实现取消/重试机制感到难过,为简洁起见,它主要是伪代码:

class CLI {
    async void Main() {
        var response = await bot.GetSomething(); // final usage
    }
}

class Bot {
    private void StartTask() {
      _taskCompSource = new TaskCompletionSource<object>();
    }

    private async Task<T> ResultPromise<T>() {
      return await _taskCompSource.Task.ContinueWith(t => t.Result != null ? (T)t.Result : default(T));
    }

    // send request
    public Task<string> GetSomething() {
        StartTask();
        QueueRequest?.Invoke(this, new Request(...)); // complex non-sync request, fired off here
        return ResultPromise<string>();
    }

    // receive response
    public void Process(Reply reply) {
        // process reply
        _taskCompSource.SetResult("success");
    }
}

class BotManager {
    // bot.QueueRequest += QueueRequest;
    // _service.ReplyReceived += ReplyReceived;

    private void QueueRequest(object sender, Request request) {
      _service.QueueRequestForSending(request);
    }

    private async void ReplyReceived(object sender, Reply reply) {
      GetBot().Process(reply);
    }
}

class Service {
    private Dictionary<string, Queue<Request>> _queues;

    // send receive loop
    private async void HttpCallback(IAsyncResult result) {
       while (true) {
        // if reply received then call ReplyReceived?.Invoke(this, reply);
        // check if request already in progress // ##ISSUE IS HERE##
        // if not send another request
        // keep connection to server open by feeding spaces
       }
    }

    public void QueueRequestForSending(Request request) {
      GetQueueForBot().Enqueue(request);
    }
}

我想在await bot.GetSomething();上实现超时但不知道如何使用这种断开连接的特性来实现它。我试过了:

static class Extensions {
    private static async Task<T> RunTaskWithRetry<T>(Func<Task<T>> taskFunc, int retries, int timeout) {
      do {
        var task = taskFunc(); // just adds another request to queue which never gets ran because the previous one is waited forever to return, this needs to cancel the previous request, from here, but how?

        await Task.WhenAny(task, Task.Delay(timeout)).ConfigureAwait(false);
        if (task.Status == TaskStatus.RanToCompletion) {
          return task.Result;
        }

        retries--;
      }
      while (retries > 0);

      return default(T);
    }

    public async static Task<T> WithRetry<T>(this Task<T> task, int retries = 3, int timeout = 10000) {
      return await RunTaskWithRetry(async () => await task, retries, timeout);
    }
}

这可以像await bot.GetSomething().WithRetry();一样调用,但这里的问题是它只是向队列添加了另一个请求而不是取消或删除现有的请求。要取消,我只需要从awaiting列表中删除现有请求。问题是我不知道如何从扩展方法的位置一直这样做。

我想知道任何可用于实现超时和取消的机制。

c# async-await timeout task cancellation
2个回答
0
投票

我不知道如何从扩展方法的位置一直这样做。

控制TaskCompletionSource<T>的代码也必须控制它的取消。该代码可以使用TaskCompletionSource<T>.TrySetCanceled实现。如果你还需要取消StartTask操作,那么你可能想要模拟那个using CancellationToken.Register。我有一个类似的async wait queue,除了它管理TaskCompletionSource<T>实例的队列而不是一个。

另一方面,重试逻辑是不确定的。 WhenAny的结果是完成的任务,所以检查Status是没有必要的;和Result应该用await替换,以避免AggregateException包装:

var completed = await Task.WhenAny(task, Task.Delay(timeout)).ConfigureAwait(false);
if (task == completed) {
  return await task;
}

-2
投票

您可以使用Task代替等待此建议

  Task responseTask = bot.GetSomething(); // final usage
  responseTask.Wait(5000); //Timeout in 5 seconds.
  var result = responseTask.Result;

如果你想使用更复杂的系统,如取消政策,请阅读这篇文章,可能会帮助你:https://johnthiriet.com/cancel-asynchronous-operation-in-csharp/

© www.soinside.com 2019 - 2024. All rights reserved.