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

时间:2019-04-09 10:32:36

标签: c# async-await timeout task cancellation

我很困惑如何为以下代码实现取消/重试机制,为简洁起见,大多数情况下都是伪代码:

<li [style.visibility]="showEmailToggle ? 'visible' : 'hidden'"></li>

我想在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();列表中删除现有请求即可。问题是我不知道如何从扩展方法所在的位置一直做到这一点。

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

2 个答案:

答案 0 :(得分: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;
}

答案 1 :(得分:-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/