Threading.Thread.Sleep() 抛出意外异常

Threading.Thread.Sleep() throws unexpected exceptions

提问人:Henrik R Clausen 提问时间:7/17/2023 最后编辑:Henrik R Clausen 更新时间:7/18/2023 访问量:44

问:

在一个大容量(每天 1-200 万个 http 请求)的 Web 应用程序(SCORM 播放器)中,我们有一个队列来优雅地处理峰值负载 - 在峰值负载下,请求可能需要等待 20-50 毫秒才能在应用程序实例之间共享的队列中得到服务。这是下面代码中对 Sleep() 的调用。奇怪的是,偶尔(每天 100-150 次)调用 Sleep() 会抛出一个空引用异常,该异常不会被 try/catch 捕获。我们将其记录到 Application Insights。

  • Sleep() 如何抛出这个未记录的异常?Threding.Thread 可以为 null 吗?
  • try/catch 怎么会无法捕获异常?
{
   "Message":"Object reference not set to an instance of an object.",
   "Data":[
      
   ],
   "InnerException":null,
   "TargetSite":"Void WaitInQueue(System.Guid, SCORM.LmsInstance)",
   "StackTrace":"   at SCORM.API.WaitInQueue(Guid key, LmsInstance instance) in D:\\a\\1\\s\\SCORM\\API.ashx.vb:line 57",
   "HelpLink":null,
   "Source":"SCORM",
   "HResult":-2147467261,
   "_typeTag":"NullReferenceException"
}
Private Shared Lock As New Object

Sub ProcessRequest(context As HttpContext) Implements IHttpHandler.ProcessRequest
    Dim response As HttpResponse = context.Response
    response.ContentType = "text/plain"
    Dim myLmsInstance As New LmsInstance With {
        .RequestData = New ApiRequestData(context.Request)
    }
    myLmsInstance.InitializeSessionData()

    Dim key As Guid = Guid.NewGuid
    MaintainQueue(True, key, myLmsInstance)
    WaitInQueue(key, myLmsInstance)
    myLmsInstance.HandleApiCall()
    MaintainQueue(False, key, myLmsInstance)
    response.Write(myLmsInstance.GetResponseData)
End Sub

Private Sub MaintainQueue(addToQueue As Boolean, key As Guid, instance As LmsInstance)
    SyncLock Lock
        If instance.SessionData IsNot Nothing Then
            If addToQueue Then
                instance.SessionData.RequestQueue.Add(key)
            Else
                instance.SessionData.RequestQueue.Remove(key)
            End If
            Dim NextInQueue = instance.SessionData.RequestQueue.FirstOrDefault
            If NextInQueue <> Guid.Empty Then
                instance.SessionData.NextInQueue = NextInQueue
            Else
                instance.SessionData.NextInQueue = Nothing
            End If
        End If
    End SyncLock
End Sub

Private Sub WaitInQueue(key As Guid, instance As LmsInstance)
    If instance.SessionData IsNot Nothing Then
        Try
            Dim NextInQueue = instance.SessionData.NextInQueue
            While NextInQueue.HasValue AndAlso NextInQueue.Value <> key
                ' Sleep as briefly as possible. The default on a Windows server is 15 ms.
                Threading.Thread.Sleep(1)
                NextInQueue = instance.SessionData.NextInQueue
            End While
        Catch ex As Exception
            Serilog.Log.Logger.Warning(ex, "Exception in SCORM Player, WaitInQueue():  {@ex}", ex)
        End Try
    End If
End Sub

最近添加了 try/catch,期望处理异常并为我们提供日志记录。但是异常仍未处理,永远不会到达 Logger.Warning() 代码。

.NET vb.net 多线程异常 并发

评论

2赞 Charlieface 7/17/2023
我冒昧地猜测实际抛出的是看起来没有任何锁定/互锁以防止并发问题的线。你可能应该在那里有一些东西,尽管目前还不清楚已经做了什么?另一个想法:您可能应该使用并避免将整个线程与每个请求捆绑在一起。NextInQueue = instance.SessionData.NextInQueueSessionDataNextInQueueasyncawait Task.Delay
0赞 Henrik R Clausen 7/17/2023
感谢您的超快速响应!我可能应该包含更多代码。实例在本地实例化,并且有锁定。我会看看我是否可以编辑帖子以包含该代码。是的,它看起来确实像一个选项,即异常中给出的行号相差几行,这解释了为什么 try/catch 没有。做好它的工作。

答:

0赞 Henrik R Clausen 7/17/2023 #1

我想我明白了(在高负载下等待验证):

Threading.Thread.Sleep(1) 是错误的,我缺少我的 Imports 语句。我将其更正为以下内容:

导入 System.Threading

线程睡眠(1)

评论

3赞 djv 7/17/2023
Threading.Thread.Sleep(1)应该不会错。这两个选项都指向相同的命名空间和函数。实际上,在调用中包含更多命名空间通常是消除调用中歧义的一种方式,因此我看不出您的编辑实际上可以改进任何内容。你能解释一下为什么你认为这将解决问题吗?