如何在使用Task.WhenAny来计算超时时获取任务的结果

时间:2017-01-29 19:07:18

标签: c# xamarin

我需要为移动应用中的任务调用添加超时功能。我尝试使用Task.WhenAny完成此操作,如下所示。这将返回首先完成的任务。我的问题是,最初我从这个任务获得了返回值,如果任务没有超时,我仍然需要它。

prob_end[3]

响应只是首先完成的任务。我如何得到它的结果?

4 个答案:

答案 0 :(得分:1)

但有一个问题,您的CancellationTokenSource如何创建和初始化,以及何时在其上调用Cancel

如果您的方法GetRequestAsync接受CancellationToken,则效果最佳。如果可能,请始终选择,因为您可以创建一个CancellationTokenSource,在设定的时间段后启动取消。会保存您对Task.WhenAny的调用。

一般来说,有多种选择,一种如下所示:

// Set timeout of 1 second
CancellationTokenSource cts = new CancellationTokenSource();
cts.CancelAfter(TimeSpan.FromSeconds(1)); 

...

Task task = restService.GetRequestAsync(articleAdr, articleParams);

// Wait until timeout or request is done.
await Task.WhenAny(task, Task.Delay(TimeSpan.FromMilliseconds(-1), cts.Token));

// If the cancellation is not yet requested the request was done before the timeout was reached
if(!cts.IsCancellationRequested)
{
    var response = await task;  
} 

另一种选择是:

Task requestTask = restService.GetRequestAsync(articleAdr, articleParams);
var firstCompletedTask = await Task.WhenAny(requestTask, Task.Delay(1000, cts.Token));
if(firstCompletedTask == requestTask)
{
    cts.Cancel(); // optionally, will cancel the delay task since it is of no use any more.
    var response = await requestTask;
}

可以根据需要多次等待完成的任务,并且它总会产生相同的结果。

答案 1 :(得分:1)

我可以想到这种情况有三种不同的可能性。

前两个可以在Peter Bons' answer中找到。

第三个是存储您的两个任务,然后在QtObject { readonly property bool foobar: someExpression onFoobarChanged: { if (foobar) { ... } } } 完成后检查状态。

await Task.WhenAny()

答案 2 :(得分:1)

我想你可以看看@jamesmontemagno MVVM Helpers。有一个extesion可以帮助您为任务添加超时

MVVM Helpers - Utils

在这里,您可以找到詹姆斯解释如何使用它的视频

The-Xamarin-Show-12-MVVM-Helpers

(接近26:38分)

答案 3 :(得分:0)

  

我需要为移动应用中的任务调用添加超时功能。我尝试使用Task.WhenAny完成此操作,如下所示。

首先,您应该知道,如果不将CancellationToken传递给GetRequestAsync,您实际上并未取消该请求。它将继续处理。

其次,我觉得你的代码比较奇怪,因为在当前状态下可能有两个超时:Task.Delay可能完成,或者CancellationToken可能会发出信号。其中一个(Task.Delay)是正常任务完成,另一个(CancellationToken)是真正的取消。

如果CancellationToken 您的超时,那么您可以使用WaitAsync库中的Nito.AsyncEx.Tasks方法:

Task task = restService.GetRequestAsync(articleAdr, articleParams);
await task.WaitAsync(cts.Token);
var result = await task;

如果CancellationToken是用户请求的取消,并且Task.Delay是您要申请的超时,那么我建议您将超时建模为另一种取消:< / p>

Task task = restService.GetRequestAsync(articleAdr, articleParams);
using (var timeoutCts = CancellationTokenSource.CreateLinkedTokenSource(cts.Token))
{
  timeoutCts.CancelAfter(1000);
  await task.WaitAsync(timeoutCts.Token);
}
var result = await task;

如果您不想使用Nito.AsyncEx.Tasks,那么您最好的选择可能是这样的(假设Task.Delay是暂停,CancellationToken是用户取消请求):

Task task = restService.GetRequestAsync(articleAdr, articleParams);
var completed = await Task.WhenAny(task, Task.Delay(1000, cts.Token));
if (completed != task)
  throw new OperationCanceledException();
var result = await task;