我创建了下面的函数,它将等待所有任务完成或在发生取消或超时时引发异常。
public static async Task WhenAll(
IEnumerable<Task> tasks,
CancellationToken cancellationToken,
int millisecondsTimeOut)
{
Task timeoutTask = Task.Delay(millisecondsTimeOut, cancellationToken);
Task completedTask = await Task.WhenAny(
Task.WhenAll(tasks),
timeoutTask
);
if (completedTask == timeoutTask)
{
throw new TimeoutException();
}
}
如果所有tasks
在很长时间之前完成(即millisecondsTimeOut
= 60,000),那么即使在函数返回后,timeoutTask
会一直待到60秒吗?如果是,解决失控问题的最佳方法是什么?
答案 0 :(得分:2)
是的,timeoutTask
会暂停,直到超时结束(或CancellationToken
被取消)。
您可以通过传递使用CancellationToken
创建的新CancellationTokenSource
获得的其他CancellationTokenSource.CreateLinkedTokenSource
并在结束时取消来修复此问题。您还应该等待完成的任务,否则您不会真正观察异常(或取消):
public static async Task WhenAll(
IEnumerable<Task> tasks,
CancellationToken cancellationToken,
int millisecondsTimeOut)
{
var cancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
var timeoutTask = Task.Delay(millisecondsTimeOut, cancellationTokenSource.Token);
var completedTask = await Task.WhenAny(Task.WhenAll(tasks), timeoutTask);
if (completedTask == timeoutTask)
{
throw new TimeoutException();
}
cancellationTokenSource.Cancel();
await completedTask;
}
但是,如果您不需要区分TimeoutException
和TaskCancelledException
,我认为这是实现您想要的更简单的方法。您只需添加在取消CancellationToken
或超时结束时取消的延续:
public static Task WhenAll(
IEnumerable<Task> tasks,
CancellationToken cancellationToken,
int millisecondsTimeOut)
{
var cancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
cancellationTokenSource.CancelAfter(millisecondsTimeOut);
return Task.WhenAll(tasks).ContinueWith(
_ => _.GetAwaiter().GetResult(),
cancellationTokenSource.Token,
TaskContinuationOptions.ExecuteSynchronously,
TaskScheduler.Default);
}