Task.Wait不等待

4

我要在VB.NET中开始一个新的任务,并等待一段时间。如果在那段时间内任务未完成,我希望附加另一个任务作为续接,并在其中记录任务1执行的经过时间。代码看起来大致如下:

Dim task As Task(Of Availability) = task.Factory.StartNew(
                                         Function() As Availability
                                             Return _GetAvailability(requests)
                                         End Function)
task.Wait(timeout)

If task.IsCompleted Then
    MyBase.availability = task.Result
Else
    task.ContinueWith(Sub(previous)
                         LogAvailabilityTimeout(timeout, elapsedTime)
                      End Sub, TaskContinuationOptions.OnlyOnRanToCompletion)
End If

由于某种原因,偶尔的task.Wait(timeout)不会按照指定的时间等待,而是直接跳过。由于此时任务未完成,因此将LogAvailabilityTimeout任务作为其继续任务附加到它。
这会导致类似以下的日志:
任务超时! 超时时间:10000ms,已用时间:3371ms
我已经设置了异常记录等,并确认没有异常和错误。看起来只是task.Wait(timeout)随机决定不等待...
有人遇到过这种情况吗? 有什么解决方法吗? 感谢您的时间。

---- 编辑 ----

好的,所以我做了一个实验,添加了一个秒表并将一些内容输出到调试中:

Dim swTemp As StopWatch = StopWatch.StartNew()
Dim task As Task(Of Availability) = task.Factory.StartNew(
                                         Function() As Availability
                                             Return _GetAvailability(requests)
                                         End Function)
task.Wait(timeout)

swTemp.Stop()
Debug.WriteLine("Waited For: " & swTemp.ElapsedMilliseconds)

If task.IsCompleted Then
    MyBase.availability = task.Result
Else
    task.ContinueWith(Sub(previous)
                         LogAvailabilityTimeout(timeout, elapsedTime)
                      End Sub, TaskContinuationOptions.OnlyOnRanToCompletion)
End If

_GetAvailability 看起来大致如下:

Private Function _GetAvailability(requests As RequestsCollection) As Availability
    Dim swElapsed As New StopWatch = StopWatch.StartNew()
    'do some stuff here (CPU heavy and network related)
    swElapsed.Stop()
    Debug.WriteLine("Elapsed: " & swElapsed.ElapsedMilliseconds)
    Me.elapsedTime = swElapsed.ElapsedMilliseconds
    Return xxx
End Function

看起来它确实在等待10000,但是在_GetAvailability内部开始之前存在大延迟(swElapsed开始)。
因此我发现TaskFactory.StartNew()并不总是立即启动任务,如果线程池已满,则任务将被放入队列中。虽然这可以理解,但Task.Wait()显然没有考虑到这一点,因此任务可能在等待完成后才开始。我正在阅读关于TaskScheduler的内容,以查看是否可以增加线程池以适应所有任务,或者做其他事情来解决问题(但目前我还不知道是什么)...如果你们有任何想法,请让我知道:)

----编辑----

为女士们和先生们提供另一个更新。线程池填充的问题已通过使用TaskCreationOptions.LongRunning提示新任务创建进行了解决。根据MS文档介绍,这提示TaskScheduler任务可能会阻塞线程池更长的时间,在这种情况下,调度程序会创建额外的线程来容纳它。这就是我现在如何触发任务的方式:

Dim task As Task(Of Availability) = task.Factory.StartNew(
                                         Function() As Availability
                                             Return _GetAvailability(requests)
                                         End Function, TaskCreationOptions.LongRunning)
task.Wait(timeout)

自从这个变化以后,所有的任务似乎都在启动时执行(没有被放入队列)。这是在我执行大约50个任务的测试中发现的。现在我要进行更多的负载测试(1000+个任务)。


“timeout” 是一个常量还是可能会被更新?此外,“elapsedTime” 是如何计算的? - Damien_The_Unbeliever
1
一个类变量,你说。它的值有可能被另一个任务覆盖吗? - Daniel Hilgarth
抱歉,不是类变量,是实例变量。无论如何,我已将超时时间更改为一个常量,并硬编码了10000的值,但仍然存在问题。我会检查其他内容,并通过更新我的代码进行报告... - Daniel Gruszczyk
2
你的更新可能值得作为一个答案 - 你已经正确地诊断出,在调用Wait()时,没有保证任务已经开始,而你提供的超时时间是当前线程愿意等待的时间,而不是应该给予任务的时间。 - Damien_The_Unbeliever
我无法做到这一点,因为它是“搜索引擎”的一部分。每个用户的搜索请求会触发大约50个不同服务供应商的搜索,然后将所有这些搜索结果聚合并显示给用户。如果平均有100个用户在搜索,那么就会有大约5000个搜索请求。我可能会改变它的实现方式,但我只能限于维护和修复现有系统的问题... - Daniel Gruszczyk
显示剩余2条评论
1个回答

1
您可以将您的函数更改如下:

Private Function _GetAvailability(requests As RequestsCollection) As Tuple(Of Availability, Long)
    Dim swElapsed As New StopWatch = StopWatch.StartNew()
    'do some stuff here (CPU heavy and network related)
    swElapsed.Stop()
    Debug.WriteLine("Elapsed: " & swElapsed.ElapsedMilliseconds)
    Return Tuple.Create(xxx,swElapsed.ElapsedMilliseconds)
End Function

然后,始终附加您的连续方法:
task.ContinueWith(Sub(previous)
    If previous.Item2 > timeout
         LogAvailabilityTimeout(timeout, previous.Item2)
    End If
End Sub, TaskContinuationOptions.OnlyOnRanToCompletion)

这应该至少解决了您的报告问题。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接