Tuesday, October 26, 2004: Understanding Chris Sells Teachings about
Multi-Threading in Windows Forms: The Importance of Multi-Threading Handlers
Chapter 14 of Chris Sells, his book Windows
Forms Programming in Visual Basic .NET (with Justin Gehtland on drums)
explains all we need to know about implementing multi-threading designs in all
but the most complex Windows Forms. Unfortunately, I had to read this chapter
about five times and I had to send several stern emails to Chris Sells (which
he generously answered) before I understood these basic bits:
·
Creating a worker thread can be an indirect
procedure (by calling BeginInvoke)
or it can be a direct procedure by getting a new Threading.Thread object. Sells discuses the disadvantages
of using the latter method but I find I am able to name a new Threading.Thread object and sets is
priority—I will tentatively call these “advantages.”
·
Think about implementing multi-threading
handlers in the same manner you think about implementing error handling: every
method that interacts with the UI thread (and is likely to be called from a worker thread) must be designed with a
pattern that interrogates the Boolean InvokeRequired
for true. The following is
the Chris Sells multi-threading handler design pattern (written with my sense
of style):
Delegate Sub MySubDelegate(ByRef WinForm As MyNameSpace.MainForm)
Sub MySub(ByRef WinForm As MyNameSpace.MainForm)
If WinForm.InvokeRequired Then
Dim del As MySubDelegate _
= New MySubDelegate(AddressOf MySub)
Call WinForm.BeginInvoke(del, New Object() {WinForm})
Else
With WinForm
'TODO: Do stuff to objects on the UI Thread.
End With
End If
End Sub
'How will Windows Forms 2.0 eliminate the need for this pattern?
·
When InvokeRequired
is always true, the flawed
design is probably calling Invoke()
or BeginInvoke() from an
object created on the worker thread. The design pattern above avoids making
this mistake by clearly showing that it is WinForm
making the call (there is, believe me, the temptation to use del.BeginInvoke, which is incorrect—it
puts wrinkles in Sells’ Burning
Man kilt!).
·
Calling back to the UI thread from the worker
thread means calling from an object that ultimately derives from System.Windows.Forms.Control that was
instantiated by the UI thread.
·
The ISynchronizeInvoke
interface contains the aforementioned worker-thread-to-UI-thread method
invocations and the Boolean indicator that such an invocation is required. As
of this writing, only one class implements this interface System.Windows.Forms.Control. Explaining
why the ISynchronizeInvoke interface
exists in the first damn place would have gone a long way toward why
multi-threading handlers are important.
·
There is the temptation to make all Windows
Forms event handlers multi-threading handlers as well. I find that this design
goal creates too much code what with the writing of delegates for every single
event handler. Another way is to not set form control properties directly in
event handlers but to create a Client Layer of static methods that set Windows
Forms controls, each with their delegates and multi-threading handlers.
·
When all of the above bits are found to be valid
and not addressed by Windows Forms 2.0 kudos to the brilliant author who thunk
it all up! Until then, these ideas are all my fault. Sorry, Chris.