我想等一个Task<T>完成一些特殊规则:如果它在Y毫秒后没有完成't completed after X milliseconds, I want to display a message to the user. And if it hasn't,我想自动request cancellation .
我可以使用Task.ContinueWith来异步等待任务完成(即,在任务完成时安排执行操作),但这不允许指定超时 . 我可以使用Task.Wait同步等待任务完成超时,但是阻止了我的线程 . 如何异步等待任务完成超时?
12 回答
您可以使用
Task.WaitAny
等待多个任务中的第一个 .您可以创建两个额外的任务(在指定的超时后完成),然后使用
WaitAny
等待先完成的任何一个 . 如果首先完成的任务是您的"work"任务,那么您就完成了 . 如果首先完成的任务是超时任务,那么您可以对超时做出反应(例如请求取消) .这个怎么样:
这是a great blog post "Crafting a Task.TimeoutAfter Method" (from MS Parallel Library team) with more info on this sort of thing .
Addition :根据对我的回答发表评论的请求,这是一个扩展的解决方案,其中包括取消处理 . 请注意,将取消传递给任务和计时器意味着可以通过多种方式在您的代码中体验取消,并且您应该确保测试并确信您正确处理了所有这些 . 不要忘记各种组合,并希望您的计算机在运行时做正确的事情 .
这是一个扩展方法版本,它结合了原始任务完成时取消超时,正如Andrew Arnott在his answer的评论中所建议的那样 .
这样的事情怎么样?
您可以使用Task.Wait选项而不使用另一个任务阻止主线程 .
这是一个基于最高投票答案的完整工作示例,它是:
此答案中实现的主要优点是已添加泛型,因此函数(或任务)可以返回值 . 这意味着任何现有函数都可以包含在超时函数中,例如:
之前:
后:
此代码需要.NET 4.5 .
Caveats
给出了这个答案之后,在正常操作期间在代码中抛出异常通常不是一个好习惯,除非你绝对必须:
每次抛出异常时,它都是极其重量级的操作,
如果异常处于紧密循环中,异常会使代码减慢100倍或更多 .
如果您绝对无法更改正在调用的函数,请仅使用此代码,以便在特定
TimeSpan
之后超时 .这个答案实际上只适用于处理您无法重构以包含超时参数的第三方库库 .
How to write robust code
如果你想编写健壮的代码,一般规则如下:
如果您没有遵守此规则,您的代码最终会遇到因某些原因失败的操作,然后它将无限期地阻止,并且您的应用程序刚刚永久挂起 .
如果在一段时间后有一个合理的超时,那么你的应用程序会挂起一段极端的时间(例如30秒)然后它会显示错误并继续其快乐方式,或重试 .
使用Timer处理消息并自动取消 . 任务完成后,在计时器上调用Dispose,以便它们永远不会触发 . 这是一个例子;将taskDelay更改为500,1500或2500以查看不同的情况:
此外,Async CTP提供了一个TaskEx.Delay方法,该方法将为您分配任务中的计时器 . 这可以为您提供更多控制,例如在Timer触发时设置TaskScheduler以进行延续 .
解决此问题的另一种方法是使用Reactive Extensions:
在单元测试中使用以下代码进行上述测试,它适用于我
您可能需要以下命名空间:
使用Stephen Cleary优秀的AsyncEx库,您可以:
如果超时,将抛出
TaskCanceledException
.@ Kevan在上面使用Reactive Extensions回答的通用版本 .
使用可选的Scheduler:
BTW:当超时发生时,超时异常将是抛出
Andrew Arnott的几个变种答案:
另一个评论,如果没有发生超时,这些版本将取消定时器,因此多次调用不会导致定时器堆积 .
SJB
如果使用BlockingCollection来计划任务,则 生产环境 者可以运行可能长时间运行的任务,并且使用者可以使用内置超时和取消令牌的TryTake方法 .
我感觉
Task.Delay()
任务和CancellationTokenSource
在另一个答案中对我的用例在一个紧凑的网络循环中有点多 .虽然Joe Hoag's Crafting a Task.TimeoutAfter Method on MSDN blogs鼓舞人心,但由于与上述相同的原因,我对使用
TimeoutException
进行流量控制感到有些厌倦,因为预计会更频繁地超时 .所以我选择了这个,它还处理了博客中提到的优化:
一个示例用例是这样的: