Threading.Thread.Sleep()抛出未捕获空引用异常问题排查
问题解答
1. Sleep()为何抛出未文档化异常?Threading.Thread是否可能为null?
- Thread.Sleep()不会抛出空引用异常:根据.NET官方文档,
Thread.Sleep(Int32)仅会在输入参数为负数时抛出ArgumentOutOfRangeException,不存在抛出NullReferenceException的场景。 - Threading.Thread不可能为null:你调用的是
Thread类的静态方法Sleep(),无需实例化Thread对象,因此不存在Thread实例为null的情况。 - 空引用异常的真实根源:多线程竞态条件。在高并发场景下,
WaitInQueue方法中虽然初始判断了instance.SessionData IsNot Nothing,但在代码执行过程中(比如Sleep()等待期间),其他线程(如处理同一Session的其他请求线程)可能将instance.SessionData置为null。当代码执行到NextInQueue = instance.SessionData.NextInQueue时,instance.SessionData已变为null,从而触发空引用异常——错误堆栈显示的行号正是这行代码,而非Sleep()调用。
2. try/catch为何无法捕获该异常?
异常未被捕获的核心原因是竞态条件导致异常发生在try块的覆盖盲区:
- 原代码中,
If instance.SessionData IsNot Nothing判断与try块之间存在时间窗口,此时其他线程可能将instance.SessionData置为null。当代码进入try块执行Dim NextInQueue = instance.SessionData.NextInQueue时,instance.SessionData已为null,抛出的异常本应被catch捕获,但在极端高并发场景下,线程上下文切换可能导致异常抛出时机超出预期; - 另一种极小概率情况是,
Sleep()期间请求上下文被意外回收,导致instance对象状态被破坏,此时抛出的异常可能绕过常规try/catch逻辑。
修复方案
核心思路:消除竞态条件,确保SessionData访问的线程安全
由于MaintainQueue方法使用SyncLock Lock保证队列操作的线程安全,WaitInQueue中访问SessionData时也需同步锁,同时避免持有锁期间Sleep阻塞其他线程:
Private Sub WaitInQueue(key As Guid, instance As LmsInstance) SyncLock Lock Dim nextInQueue As Guid? = Nothing If instance.SessionData IsNot Nothing Then nextInQueue = instance.SessionData.NextInQueue End If While nextInQueue.HasValue AndAlso nextInQueue.Value <> key ' 释放锁后再Sleep,避免阻塞其他队列操作 Monitor.Exit(Lock) Threading.Thread.Sleep(1) ' 重新获取锁,检查SessionData状态 Monitor.Enter(Lock) If instance.SessionData Is Nothing Then Exit While ' Session已失效,直接退出等待 End If nextInQueue = instance.SessionData.NextInQueue End While End SyncLock End Sub
额外优化:增强异常捕获的覆盖率
调整try/catch块的范围,覆盖整个方法的核心逻辑,确保任何异常都能被捕获:
Private Sub WaitInQueue(key As Guid, instance As LmsInstance) Try SyncLock Lock Dim nextInQueue As Guid? = Nothing If instance.SessionData IsNot Nothing Then nextInQueue = instance.SessionData.NextInQueue End If While nextInQueue.HasValue AndAlso nextInQueue.Value <> key Monitor.Exit(Lock) Threading.Thread.Sleep(1) Monitor.Enter(Lock) If instance.SessionData Is Nothing Then Exit While End If nextInQueue = instance.SessionData.NextInQueue End While End SyncLock Catch ex As Exception Serilog.Log.Logger.Warning(ex, "Exception in WaitInQueue for key {Key}", key) End Try End Sub
内容的提问来源于stack exchange,提问作者Henrik R Clausen
相关产品推荐
相关产品推荐

