所以我有一个游戏,其中主线程执行您期望的通常的更新/渲染逻辑,而第二个线程执行一些非常密集的处理。我遇到的问题是偶尔主线程会被中断,游戏会降到 60FPS 以下。我相当确定它被另一个线程阻塞了,但是由于没有显式锁定,我无法证明它。
关于为什么主线程会被辅助线程阻塞,我可以想到几种情况:
- 第二个线程分配了很多小对象;内存分配迫使一个线程等待,而另一个线程分配内存。这似乎不太可能,因为您期望在分配一个小对象之后,主线程可以继续分配它需要的东西。
- 某种形式的 JIT 优化可防止辅助线程在耗时过长时被中断。这根本没有意义。
- 某种被锁定的跨线程引用。不太可能,因为代码被一个队列有意分隔,在该队列中辅助线程从队列中取出项目,但不锁定并阻止项目被放置在队列中。
- 操作系统的错误线程优先级,也不太可能,因为这个问题发生在 Linux 和 Windows 上。
我尝试过放入秒表并测量哪些代码区域需要时间,但这并不能真正告诉我“主线程随机停止了 500 毫秒”;它实际上并没有告诉我是否有锁长时间阻塞主线程。
有什么技术可以用来缩小这个问题的原因吗?
- - - 编辑 - - -
这些是运行 Mono 分析器并报告锁定争用的结果:
Monitor lock summary
Lock object 0x7f05190c9fe0: 1 contentions
0.002126 secs total wait time, 0.002126 max, 0.002126 average
1 contentions from:
(wrapper runtime-invoke) object:runtime_invoke_void__this__ (object,intptr,intptr,intptr)
System.Threading.Thread:StartInternal ()
System.Threading.Timer/Scheduler:SchedulerThread ()
(wrapper unknown) System.Threading.Monitor:FastMonitorEnterV4 (object,bool&)
System.Threading.Monitor:Enter (object,bool&)
System.Threading.Monitor:TryEnter (object,int,bool&)
(wrapper managed-to-native) System.Threading.Monitor:try_enter_with_atomic_var (object,int,bool&)
Lock object 0x7f051910b100: 1 contentions
0.000628 secs total wait time, 0.000628 max, 0.000628 average
1 contentions from:
Ninject.Components.ComponentContainer:Get (System.Type)
Ninject.Components.ComponentContainer:ResolveInstance (System.Type,System.Type)
Ninject.Components.ComponentContainer:CreateNewInstance (System.Type,System.Type)
System.Reflection.ConstructorInfo:Invoke (object[])
System.Reflection.MonoCMethod:Invoke (System.Reflection.BindingFlags,System.Reflection.Binder,object[],System.Globalization.CultureInfo)
System.Reflection.MonoCMethod:DoInvoke (object,System.Reflection.BindingFlags,System.Reflection.Binder,object[],System.Globalization.CultureInfo)
System.Reflection.MonoCMethod:InternalInvoke (object,object[])
(wrapper managed-to-native) System.Reflection.MonoCMethod:InternalInvoke (System.Reflection.MonoCMethod,object,object[],System.Exception&)
(wrapper runtime-invoke) <Module>:runtime_invoke_void__this___object (object,intptr,intptr,intptr)
Ninject.Activation.Caching.ActivationCache:.ctor (Ninject.Activation.Caching.ICachePruner)
Ninject.Activation.Caching.GarbageCollectionCachePruner:Start (Ninject.Activation.Caching.IPruneable)
(wrapper remoting-invoke-with-check) System.Threading.Timer:.ctor (System.Threading.TimerCallback,object,int,int)
System.Threading.Timer:.ctor (System.Threading.TimerCallback,object,int,int)
System.Threading.Timer:Init (System.Threading.TimerCallback,object,long,long)
System.Threading.Timer:Change (long,long,bool)
System.Threading.Timer/Scheduler:Change (System.Threading.Timer,long)
(wrapper unknown) System.Threading.Monitor:FastMonitorEnterV4 (object,bool&)
System.Threading.Monitor:Enter (object,bool&)
System.Threading.Monitor:TryEnter (object,int,bool&)
(wrapper managed-to-native) System.Threading.Monitor:try_enter_with_atomic_var (object,int,bool&)
Lock object 0x7f05190ca000: 1 contentions
0.000347 secs total wait time, 0.000347 max, 0.000347 average
1 contentions from:
(wrapper runtime-invoke) object:runtime_invoke_void__this__ (object,intptr,intptr,intptr)
System.Threading.Thread:StartInternal ()
System.Threading.Timer/Scheduler:SchedulerThread ()
(wrapper remoting-invoke-with-check) System.Threading.EventWaitHandle:Reset ()
System.Threading.EventWaitHandle:Reset ()
(wrapper unknown) System.Threading.Monitor:FastMonitorEnterV4 (object,bool&)
System.Threading.Monitor:Enter (object,bool&)
System.Threading.Monitor:TryEnter (object,int,bool&)
(wrapper managed-to-native) System.Threading.Monitor:try_enter_with_atomic_var (object,int,bool&)
Lock contentions: 3
Lock acquired: 3
Lock failures: 0
这是从运行游戏大约 20-30 秒得出的,在此期间我观察到至少 10 个滞后峰值。在那段时间里只有 3 次锁争用,所有这些争用都需要不到 16 毫秒的时间来解决。