"Jon Skeet [C# MVP]" wrote:
I've finally managed to finish my article on multi-threading - at least
for the moment. I'd be *very* grateful if people with any interest in
multi-threading would read it (even just bits of it - it's somewhat
long to go through the whole thing!) to check for accuracy,
effectiveness of examples, etc.
Feel free to mail me directly with comments or post them here.
I scanned through your article and it seemed very well written. I have a
little bit of feedback on a few sections that you may be interested in:
The section on volitile and stale data, I though the example and text was
good for beginners, but didn't touch on what was (is!) the biggest issue
there for me - that is the problem of "it runs great in debug mode, but when
I turn on optimizations my code doesn't work any more." issue. The way the
various optimizers can and do isolate variables inside loops and other very
subtle things has caught me by surprise on more than one occasion. Your
suggestion of wrapping things in a lock in order to avoid the 'volitile'
issue is excellent - especially as it'll work in VB.NET, which lacks the
volitile keyword.
The code samples in the "Stopping" section, I don't agree with. First the
Nitpicks:
You are using variables "stopping" and "stopped", and properties "Stopping"
and "Stopped". This naming convention shoud, I believe, be frowned upon.
Relying on case to differentiate members and properties is just asking for
trouble. It also violates the naming convention MS spells out in the MSDN
documentation for .NET.
I typically write this code as:
Private _stop As New Threading.Manua lResetEvent(Fal se)
Private _runningThread As Threading.Threa d
Public Sub StartTheThread( )
_stop.Reset()
_runningThread = New Threading.Threa d(AddressOf MyThread)
_runningThread. Start()
End Sub
Public Sub StopTheThread()
_stop.Set()
_runningThread. Join()
End Sub
Private Sub MyThread()
While Not _stop.WaitOne(0 , False)
' Do Work Here
End While
End Sub
I prefer this over your sample for a few reasons
1 - It is my belief (I have not benchmarked it, although I should) that
testing an event for Set/Not-Set is much cheaper than acquiring and
releasing a monitor (potentially) several millions times per second.
2 - If my thread loop doesn't have to run "as fast as it can", I can easilyl
change the to "While Not _stop.WaitOne(1 000, False)" to make my loop run
once per second, while at the same time being responsive to shutdown events.
In your sample code, I would need to use a Sleep to slow down the loop,
which makes the thread unresponsive to shutting down.
3 - Your code is, in essece, creating a Manual Reset Event. Why create
something that's already there?
There were a few sections that I would have liked to see in there - mostly
these are questions that I've had for a while, and haven't ever been able to
get good ansers to..
1 - Something that I've looked for information on, and found absolutly
nothing, is the "Synchroniz ed" version of the various collection classes. I
have ended up having to roll my own collections in many cases, as I need to
access them from multiple threads (I am sooooo looking forward to
Generics!). With the lack of documentation on the synchronized collection
classes, I was never comfortable using them. What about them is thread safe?
I'm pretty sure Iteration is not (although it could be - the iterator could
grab a lock, and prevent other iterators from proceeding until it's
complete). Hashtables say (somewhere) that "one writer and multiple readers"
is thread safe, but I've never been quite sure of that. What about the other
collections?
2 - Some of the Win32 threading constructs that people have been using for
so long are not present in .NET. Specifically Semaphores and (I think) Named
Events. Any idea why?
3 - Some suggestions on how to start debugging threaded code. These could be
simple suggestions like "Give all your threads unique names, so that then
you're looking at the Threads window in the debugger, you can quickly tell
which thread is which", or complex suggestions like "Use SoS to determine
which thread is holding a lock".
4 - You don't have a section on Events (either Manual or AutoReset). Along
with Monitors, these are the most used threading constructs.
5 - ReaderWriterLoc ks are handy at times, but are probably beyond the scope
of your article.
6 - Another advanded topic that I've seen little on, but seems as if it
could be really usefull, is the ability of the Threadpool to have a Handle
bound to it, and provide callbacks when this handle changed. Can I bind
standard wait handles to the thread pool so that I can callbacks whenever a
manual or auto-reset even is changed? It seems as if there would probably be
some fascinating use cases for this...
--
Chris Mullins