Scenario: The randomly freezing thread
I have a process that reads multiple text files line-by-line and updates a progress bar.
- When looping, I ran AddUserInterfaceUpdate(“Progress”,value) every 50 loops (e.g. “If ( numLoops Mod 50 ) = 0 Then” ), and my thread would randomly freeze.
- If I added a few System.DebugLog() messages, the process would last longer, but still eventually froze.
Whenever I broke into the debugger, the thread’s ThreadState said that it was Running.
-
But despite that status, if I tried to step through, the thread remained frozen (i.e. System.DebugLog lines immediately after the breakpoint were never output).
-
Initially, the symptoms resembled a recursive loop, and since I couldn’t break/step in the debugger, I added System.DebugLog messages to find where the process was freezing/hanging.
-
This proved that the thread would freeze randomly in different places, none of which made logical sense.
Cause: Too many update statements
After hours of analysis and research, I finally went into “guru meditation” and began to consider what was happening internally in the O/S:
- Remember, I was calling the AddUserInterfaceUpdate() about every 50 loops (which was still 100’s of times a second).
- In laymen’s terms, this adds the event to the “event stack,” which the O/S would allocate CPU time to “clearing out” when it deemed okay.
- But every computer has buffers, and every buffer has limits.
- I inferred that the random hangs of my thread were caused by rapid-firing the AddUserInterfaceUpdate(“Progress”,value) (too much too fast).
- I tested this hypothesis, and was able to both reproduce the hanging problem, and reproduce the solution.
Solution: Slow down the update statements
- The solution was painfully simple: I slowed the output of AddUserInterfaceUpdate() statements.
- Initially, I was outputting about ever 50 loops: “If ( numLoops Mod 50 ) = 0 Then”
- I simply slowed that to about every 500 loops: “If ( numLoops Mod 500 ) = 0 Then”
- The progress bar still progresses nicely.
- But the AddUserInterfaceUpdate() events are no longer filling the event buffer faster than it can be processed, so the thread never hangs.
Here is the method code I used:
Var progressDict As New Dictionary
Var loopOneMax As Integer = 500
Var loopTwoMax As Integer = 500
progressDict.Value("ProgressValue") = 0
progressDict.Value("ProgressTotal") = loopOneMax * loopTwoMax
Me.AddUserInterfaceUpdate(progressDict)
Var progVal As Integer
For i As Integer = 0 To loopOneMax
System.DebugLog("This is output from the loop i (EYE): " + i.ToString )
For j As Integer = 0 To loopTwoMax
progVal = progVal + 1
System.DebugLog("This is output from the loop j (JAY): " + j.ToString )
Next j
Me.AddUserInterfaceUpdate("ProgressValue" : progVal )
Next i
Again, hopefully this will help someone else avoid this frustration.