所以我有一个游戏,其中主线程执行您所期望的常规更新/渲染逻辑,第二个线程执行一些非常强烈的处理。我遇到的问题是,偶尔主线程会被中断,游戏会降低到60FPS以下。我相当确定它被另一个线程阻塞了,但由于没有显式锁定,我无法证明。
我能想到几种情况,导致主线程被次线程阻塞:
1. 第二个线程分配了大量小对象;内存分配会强制一个线程等待,而另一个线程则分配内存。这似乎不太可能发生,因为您期望在分配一个小对象之后,主线程可以继续分配所需的对象。 2. 一些JIT优化会防止次线程在执行时间过长时被中断。这毫无意义。 3. 某种正在被锁定的跨线程引用。这不太可能发生,因为该代码是有意通过队列进行分离的,在队列上,次线程拾取项目却不会锁定并阻止放置项目。 4. 操作系统的错误线程优先级,这也不太可能发生,因为这个问题在Linux和Windows上都会出现。
我尝试了加入秒表并测量哪些代码需要时间,但这并没有告诉我更多信息,只是"主线程在随机时间停止了500毫秒",它实际上并没有告诉我是否有锁定阻塞了主线程一段时间。
有什么技术可以用来缩小此问题的原因范围吗?
-----编辑-----
这些是运行Mono分析器并报告锁争用情况的结果:
我能想到几种情况,导致主线程被次线程阻塞:
1. 第二个线程分配了大量小对象;内存分配会强制一个线程等待,而另一个线程则分配内存。这似乎不太可能发生,因为您期望在分配一个小对象之后,主线程可以继续分配所需的对象。 2. 一些JIT优化会防止次线程在执行时间过长时被中断。这毫无意义。 3. 某种正在被锁定的跨线程引用。这不太可能发生,因为该代码是有意通过队列进行分离的,在队列上,次线程拾取项目却不会锁定并阻止放置项目。 4. 操作系统的错误线程优先级,这也不太可能发生,因为这个问题在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毫秒内解决。