By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
424,667 Members | 2,084 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 424,667 IT Pros & Developers. It's quick & easy.

Multi-threading article finally "finished" - reviewers welcome

P: n/a
Please excuse the cross-post - I'm pretty sure I've had interest in the
article on all the groups this is posted to.

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.

The article is at:
http://www.pobox.com/~skeet/csharp/multithreading.html

--
Jon Skeet - <sk***@pobox.com>
http://www.pobox.com/~skeet
If replying to the group, please do not mail me too
Jul 21 '05 #1
Share this Question
Share on Google+
77 Replies


P: n/a
"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.ManualResetEvent(False)
Private _runningThread As Threading.Thread

Public Sub StartTheThread()
_stop.Reset()
_runningThread = New Threading.Thread(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(1000, 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 "Synchronized" 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 - ReaderWriterLocks 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

Jul 21 '05 #2

P: n/a
Very nice article; had a quick look through it, its an easy way to explain
not-so-easy issues :)

Back in the VB days everybody wanted multi-threads (even did it with api
*highly unstable*) but now I donīt see people using it as much on .NET as it
would seem natural to.

I myself had a time and half on a multi-threading application on .NET where
I learned just how messy can it get without some sort of mutex.
Jul 21 '05 #3

P: n/a
A good addition might be a discussion of ManualResetEvent and AutoResetEvent
especially the differences between them and when they are appropriate ... I
get questions on that all the time.

Greg

"Jon Skeet [C# MVP]" <sk***@pobox.com> wrote in message
news:MP************************@msnews.microsoft.c om...
Please excuse the cross-post - I'm pretty sure I've had interest in the
article on all the groups this is posted to.

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.

The article is at:
http://www.pobox.com/~skeet/csharp/multithreading.html

--
Jon Skeet - <sk***@pobox.com>
http://www.pobox.com/~skeet
If replying to the group, please do not mail me too

Jul 21 '05 #4

P: n/a
Greg Young <gr********@planetbeach.com> wrote:
A good addition might be a discussion of ManualResetEvent and AutoResetEvent
especially the differences between them and when they are appropriate ... I
get questions on that all the time.


Right. That's two requests for them: they're definitely going in. This
is a good opportunity to get myself more thoroughly familiar with them,
too :)

--
Jon Skeet - <sk***@pobox.com>
http://www.pobox.com/~skeet
If replying to the group, please do not mail me too
Jul 21 '05 #5

P: n/a
<ca*****@hotmail.com> wrote:
Very nice article; had a quick look through it, its an easy way to explain
not-so-easy issues :)
Cool, thanks :) As with most of the articles, it was born out of
laziness: I don't want to spend ages typing the same kinds of answer
out again and again!
Back in the VB days everybody wanted multi-threads (even did it with api
*highly unstable*) but now I donīt see people using it as much on .NET as it
would seem natural to.
Interesting. I certainly cringe when Application.DoEvents is
recommended for something which naturally lends itself to multiple
threads.
I myself had a time and half on a multi-threading application on .NET where
I learned just how messy can it get without some sort of mutex.


I would laugh, but the memory of similar situations is too painful ;)

--
Jon Skeet - <sk***@pobox.com>
http://www.pobox.com/~skeet
If replying to the group, please do not mail me too
Jul 21 '05 #6

P: n/a
Chris Mullins <cm******@yahoo.com> wrote:
"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.


I can't work out whether I'm surprised that VB.NET lacks volatile or
not - I think I *may* have tried to find an equivalent and failed, but
I'm not sure.

It's a good point though - I think I've specified somewhere that "it
works for me" isn't a good enough reason to believe the code is valid,
but I'll emphasise it much more. Good catch.
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.
It doesn't - the naming convention MS uses specifically doesn't mention
private members. It's not a problem for VB users because anyone outside
the class doesn't see the variables at all.

I've used the convention for years in both Java and C# and *never* had
a problem with it as far as I can remember. I find code written with my
convention significantly easier to use than code which uses underscores
all over the place - they just look horribly ugly to me, and disturb my
train of thought when I'm reading.

I'm afraid I'll stick with my convention as I do for all my code
samples.
I typically write this code as:

Private _stop As New Threading.ManualResetEvent(False)
Private _runningThread As Threading.Thread

Public Sub StartTheThread()
_stop.Reset()
_runningThread = New Threading.Thread(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.
That's a heck of an assumption, in my view. Rather than making any
assumptions, I decided to test it. The main code in both my tests was
the same:

using System;
using System.Threading;

public class LockTest
{
static void Main()
{
Worker worker = new Worker();
new Thread(new ThreadStart(worker.Run)).Start();
Thread.Sleep(10000);
worker.Stop();
}
}

The worker object for my version is as on the website (except with
stopLock declared as object, not bool - oops! Fixed the website now)
but with a long counter variable which is just incremented in the loop
in the same way as with the code below, which is a slightly simplified
version of what you posted (just for the purposes of this test):

public class Worker
{
long counter=0;

ManualResetEvent stop = new ManualResetEvent(false);

public void Stop()
{
stop.Set();
}

public void Run()
{
DateTime start = DateTime.Now;
while (!stop.WaitOne(0, false))
{
counter++;
}
DateTime end = DateTime.Now;
Console.WriteLine
("Count per second: {0}", counter/(end-start).TotalSeconds);
}
}

Results (3 tests each):

Using a lock:
Count per second: 28038249.614792
Count per second: 28113891.7558685
Count per second: 27467033.4397496

Using a manual reset event:
Count per second: 1087057.6
Count per second: 1086289.82785602
Count per second: 1082087.3

So on average, using a lock we're able to count about 25 times as fast.

Just another example of threading not being a good thing to guess about
:)

I'd be very interested to see whether you get the same kind of results
- it may *very* well depend to a very large extent on the CPU(s) of the
test machines. Mine is a P4/3.06GHz laptop; no hyperthreading or
anything like that. I'd be particularly interested to know the numbers
on a dual processor machine...
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(1000, 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.
Well, I'd probably use Monitor.Wait in that case, myself. I'm happier
with Monitor than reset events, but either would work fine here. I
wouldn't usually limit it artificially like that though - normally if
there's a reason to slow something down, that's because it hasn't got
work the whole time - and there I'd use a work queuing system. If I
want a thread to essentially be at a lower priority, I'll just lower
the priority. (Ah - another thing I haven't covered :)
3 - Your code is, in essece, creating a Manual Reset Event. Why create
something that's already there?
It's not, really. There's much more to events than just testing and
setting, as you've already shown with WaitOne - there are also calls to
wait for potentially many events at a time, etc. I believe testing a
boolean (even in a lock) is significantly simpler and easier to
understand than ManualResetEvents, which is why I stick with them in
general.
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..
I'll present quick answers here (to a couple of them), and see if there
are other comments in this thread (no pun intended). I'll then see
which bits are appropriate for inclusion in the article itself.
1 - Something that I've looked for information on, and found absolutly
nothing, is the "Synchronized" 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?
I believe the idea of synchronized collections is that each individual
operation automatically takes out a lock, but that in order to
synchronize a whole series of operations, you need to lock on the
SyncRoot of the collection.

I've seen the same statement about multiple readers and a single writer
for hashtable, but I haven't tried to verify it with stress testing or
anything like that.
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?
I really don't know. Semaphores are handy constructs, and fairly easily
built out of monitors (so long as you're careful :) I may present a
sample semaphore class for those who are interested. I wouldn't be at
all surprised if they made it into .NET v2 anyway though.
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".
Right, that's a good idea.
4 - You don't have a section on Events (either Manual or AutoReset). Along
with Monitors, these are the most used threading constructs.
Funnily enough, I've virtually never used them - except to build a
fuller Monitor implementation for the Compact Framework :)

I suspect it's my Java background showing through to some extent, but I
more naturally think in terms of monitors, and they tend to do
everything I need them to. I think they're generally simpler, which is
another reason I use them :) (I do use Events when I want to wait for
multiple conditions etc.)
5 - ReaderWriterLocks are handy at times, but are probably beyond the scope
of your article.
Not sure - I think that's a very good idea. I haven't had to use them
myself, but almost certainly will at some time. I'll take a bit of a
breather and then probably add a section about them.
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...


Mmm... I really don't know *anything* about that topic. Sounds like
something it would be worth investigating...

--
Jon Skeet - <sk***@pobox.com>
http://www.pobox.com/~skeet
If replying to the group, please do not mail me too
Jul 21 '05 #7

P: n/a

"Jon Skeet [C# MVP]" wrote:
Chris Mullins <cm******@yahoo.com> wrote:
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.
I can't work out whether I'm surprised that VB.NET lacks volatile or
not - I think I *may* have tried to find an equivalent and failed, but
I'm not sure.


I've looked several times for it, and each time been quite shocked not
to find it. I always feel if I dig "just a bit deeper" I'll find it. Ah
well.
One more thing that occasionally forces me to write code in C#...
You are using variables "stopping" and "stopped", and properties "Stopping" and "Stopped". This naming convention shoud, I believe, be frowned upon.
[...]
I've used the convention for years in both Java and C# and *never* had
a problem with it as far as I can remember. I'm afraid I'll stick with my convention as I do for all my code
samples.
'Fair Nuff. I still think it's evil though.
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.


That's a heck of an assumption, in my view. Rather than making any
assumptions, I decided to test it.


[...]
Results (3 tests each):

Using a lock:
Count per second: 28038249.614792
Count per second: 28113891.7558685
Count per second: 27467033.4397496

Using a manual reset event:
Count per second: 1087057.6
Count per second: 1086289.82785602
Count per second: 1082087.3

So on average, using a lock we're able to count about 25 times as fast.


Wow. I should know better by now than to make assumptions on these
things. That's what Compuware's profiler is for.
1 - Something that I've looked for information on, and found absolutly
nothing, is the "Synchronized" 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?


I believe the idea of synchronized collections is that each individual
operation automatically takes out a lock, but that in order to
synchronize a whole series of operations, you need to lock on the
SyncRoot of the collection.


I'm pretty sure this is what they do too, but I've never seen this actually
documented anywhere. Their complete non-existance on the CF has meant
that I needed to write my own thread-safe collections anyway, so I've
never looked into them in great detail. (But for a threading article on the
standard CLR, I think they're a good topic)
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?


I really don't know. Semaphores are handy constructs, and fairly easily
built out of monitors (so long as you're careful :) I may present a
sample semaphore class for those who are interested. I wouldn't be at
all surprised if they made it into .NET v2 anyway though.


The one thing that Semaphores have, that I don't think you can duplicate
with Monitors, is that they're cross-process. They're really handy for "Is
my application already running?" checks, as well as a wide variety of
other computer-wide resource management tricks.
4 - You don't have a section on Events (either Manual or AutoReset). Along with Monitors, these are the most used threading constructs.


Funnily enough, I've virtually never used them - except to build a
fuller Monitor implementation for the Compact Framework :)


That's funny - I used a combination of Win32 calls and Monitors to
implement a full ManualResetevent on the compact framework.

For some silly reason there is no WaitHandle.WaitOne(Timeout, false)
overload on the CF, which makes the class pretty useless for a wide
vareity of useses.. It's either wait forever, or don't wait at all. If
you want the source to this, drop me an email some time.

(That's the least I can do for you writing that UTF32String class a few
weeks back)

--
Chris Mullins
Jul 21 '05 #8

P: n/a
Chris Mullins <cm******@yahoo.com> wrote:
I can't work out whether I'm surprised that VB.NET lacks volatile or
not - I think I *may* have tried to find an equivalent and failed, but
I'm not sure.
I've looked several times for it, and each time been quite shocked not
to find it. I always feel if I dig "just a bit deeper" I'll find it. Ah
well.
One more thing that occasionally forces me to write code in C#...


Reflector decompiles a volatile variable as having ModReq(IsVolatile)
after it, which I believe is what's in IL. It doesn't look like you can
apply that within VB though.

<big snip>
I believe the idea of synchronized collections is that each individual
operation automatically takes out a lock, but that in order to
synchronize a whole series of operations, you need to lock on the
SyncRoot of the collection.


I'm pretty sure this is what they do too, but I've never seen this actually
documented anywhere. Their complete non-existance on the CF has meant
that I needed to write my own thread-safe collections anyway, so I've
never looked into them in great detail. (But for a threading article on the
standard CLR, I think they're a good topic)


Right. I might see what else I can dig up about them - without the
source code being available, it's hard to say. I can always look at the
Rotor implementation, I suppose.
I really don't know. Semaphores are handy constructs, and fairly easily
built out of monitors (so long as you're careful :) I may present a
sample semaphore class for those who are interested. I wouldn't be at
all surprised if they made it into .NET v2 anyway though.


The one thing that Semaphores have, that I don't think you can duplicate
with Monitors, is that they're cross-process. They're really handy for "Is
my application already running?" checks, as well as a wide variety of
other computer-wide resource management tricks.


Ah - I've been thinking in terms of the computer science idea of a
counting semaphore, rather than the Win32 specific concept.

For cross-process "am I running" checks, however, a Mutex works pretty
well. See
http://www.pobox.com/~skeet/csharp/f...ation.instance

I dare say there are other uses which Mutex wouldn't cut it for though.
Funnily enough, I've virtually never used them - except to build a
fuller Monitor implementation for the Compact Framework :)


That's funny - I used a combination of Win32 calls and Monitors to
implement a full ManualResetevent on the compact framework.

For some silly reason there is no WaitHandle.WaitOne(Timeout, false)
overload on the CF, which makes the class pretty useless for a wide
vareity of useses.. It's either wait forever, or don't wait at all. If
you want the source to this, drop me an email some time.


Have you thought about contributing it to OpenNETCF? That's what I did
with what is now called MonitorEx. I had the same problem as you did
though - I can't implement Wait with a timeout. I suspect if either of
us could implement one of them, we could use it to implement the other
:)

(I had a few ideas about how to do it using timers, but the ideas
became pretty horrible.)
(That's the least I can do for you writing that UTF32String class a few
weeks back)


Ah, that was for you, was it? I couldn't remember. Did you ever use it,
or were you able to make do with a couple of bits of it? It was fun
writing it either way :) (I dread to think what that says about me...)

--
Jon Skeet - <sk***@pobox.com>
http://www.pobox.com/~skeet
If replying to the group, please do not mail me too
Jul 21 '05 #9

P: n/a
Brian Gideon <Br*********@discussions.microsoft.com> wrote:
It's been a while since I've monitored usenet, but with this slick
web-based interface at the MSDN site I might be a frequent visitor.
I think I'll always favour a "proper" newsreader app myself, but each
to their own :)
Anyway, multithreaded applications have always interested me so your
post naturally stuck out like a sore thumb. You have a very nice
article and coming from you I think we can all count on quality
information. Nice job!


Thanks - but until it's been vetted by a few people who know more than
I do, I wouldn't assume absolutely everything's right. I've tried to be
as accurate as I can, but as I state at the start of the article, I'm
far from being an expert on it. Trouble is, I don't want to bother the
real experts too much by asking them to look at it :)

--
Jon Skeet - <sk***@pobox.com>
http://www.pobox.com/~skeet
If replying to the group, please do not mail me too
Jul 21 '05 #10

P: n/a
"Jon Skeet [C# MVP]" <sk***@pobox.com> wrote in message:

[UTF32 String Class]
(That's the least I can do for you writing that UTF32String class a few
weeks back)


Ah, that was for you, was it? I couldn't remember. Did you ever use it,
or were you able to make do with a couple of bits of it? It was fun
writing it either way :) (I dread to think what that says about me...)


Unfortunatly I ended up not being able to use it, although I spent quite
some time with it before coming to that conclusion.

I needed it to perform StringPrep for some XMPP stuff, and some of the
required steps for that are pretty ugly.

The two steps that specifically made me decide not to implement the RFC
myself were the cannocial form transforms, and the KC Normalization step. I
couldn't for the life of me figure out a reliable and space-efficient way to
get all the Unicode Data tables required for that into a usable form.
They're all available for download on the Unicode site, but it's like 60
megs of data, and I really, really wasn't looking forward to writing a perl
script of some kind to transform them into .NET resource files and the like.
It was a nightmare.

I did end up learning more about Unicode than I ever wanted to know, that's
for sure!

I think I've finally settled on wrapping 'libidn' (which is a LGPL licensed
project) in a Win32 DLL, and calling that. Ick.

--
Chris Mullins
Jul 21 '05 #11

P: n/a
> Results (3 tests each):

Using a lock:
Count per second: 28038249.614792
Count per second: 28113891.7558685
Count per second: 27467033.4397496

Using a manual reset event:
Count per second: 1087057.6
Count per second: 1086289.82785602
Count per second: 1082087.3

So on average, using a lock we're able to count about 25 times as fast.

Just another example of threading not being a good thing to guess about
:)

I'd be very interested to see whether you get the same kind of results
- it may *very* well depend to a very large extent on the CPU(s) of the
test machines. Mine is a P4/3.06GHz laptop; no hyperthreading or
anything like that. I'd be particularly interested to know the numbers
on a dual processor machine...


The numbers are a bit better on my dual processor machine, but not
substantially. Looks like lock is about 10x faster on this machine.
I don't think that these results would be sufficent to state that the
differences are due to multiple processor machines so much as difference in
speeds between the processors I used and the one you used. I ran this on a
dual 2.2 ghz xeon, with hyperthreading. I was able to verify that the speed
of locks and events are as identical as benchmarks can be when affinity
targets one or two physical processors, or all four virtual processors,
however. I would guess that the speed of these sync mechanisms are pretty
much neutral to the number of processors as long as their is only one
thread. A more interesting, but more complicated, test would be to test lock
and event speed across multiple threads.

Using a lock:
Count per second: 5581104.27543036
Count per second: 5615276.9
Count per second: 5500714.86697966

Using a manual reset event:
Count per second: 500654.6
Count per second: 489649.3771518
Count per second: 494306.7

Jul 21 '05 #12

P: n/a

On 17 Jun 2004 20:58, Brian Gideon wrote:


A newsreader app is ideal, but when the outgoing NNTP port is closed you
have limited options.

<Shameless Puff - apologies>

NewsLook has the ability to delegate the calls to the NNTP server via
an HTTP call over port 80 to it's home site. Currently it's sloow as I've
not been concentrating on that part of it. If there's interest though....

Simon Smith
simon dot s at ghytred dot com
http://www.ghytred.com/NewsLook - Usenet for Outlook
Jul 21 '05 #13

P: n/a
Please please please split it out into multiple html pages - one long one is horrible.

Do you keep your .cs code files like that? No, I didn't think so! HTML deserves the same respect and for exactly the same reason, same goes for anyone reading it.
Jul 21 '05 #14

P: n/a
"triple-new" syntax is one of the great features of C# - check out:

(new Thread(new ThreadStart((new HistoricalCatchup( accessdb, drive)).StartCatchup))).Start();

an example of mine that starts a thread on a member function of a class (due to the fact that it has to have the parameters accessdb and drive)

"Jon Skeet [C# MVP]" wrote:
Please excuse the cross-post - I'm pretty sure I've had interest in the
article on all the groups this is posted to.

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.

The article is at:
http://www.pobox.com/~skeet/csharp/multithreading.html

--
Jon Skeet - <sk***@pobox.com>
http://www.pobox.com/~skeet
If replying to the group, please do not mail me too

Jul 21 '05 #15

P: n/a
Beeeeeeeeeeeeves <Be**************@discussions.microsoft.com> wrote:
Please please please split it out into multiple html pages - one long
one is horrible.
Agreed.
Do you keep your .cs code files like that? No, I didn't think so!
HTML deserves the same respect and for exactly the same reason, same
goes for anyone reading it.


The trouble is, it started off being a relatively short article, which
was fine as one page. It's definitely too long now. I'll work out
exactly how to split it up soon.

--
Jon Skeet - <sk***@pobox.com>
http://www.pobox.com/~skeet
If replying to the group, please do not mail me too
Jul 21 '05 #16

P: n/a
Beeeeeeeeeeeeves <Be**************@discussions.microsoft.com> wrote:
"triple-new" syntax is one of the great features of C# - check out:

(new Thread(new ThreadStart((new HistoricalCatchup(accessdb,
drive)).StartCatchup))).Start();

an example of mine that starts a thread on a member function of a
class (due to the fact that it has to have the parameters accessdb
and drive)


Of course, there's nothing *forcing* you to do that all on one line -
you could easily split it up.

--
Jon Skeet - <sk***@pobox.com>
http://www.pobox.com/~skeet
If replying to the group, please do not mail me too
Jul 21 '05 #17

P: n/a

"Beeeeeeeeeeeeves" <Be**************@discussions.microsoft.com> wrote in
message news:E4**********************************@microsof t.com...
Please please please split it out into multiple html pages - one long one is horrible.
Do you keep your .cs code files like that? No, I didn't think so! HTML

deserves the same respect and for exactly the same reason, same goes for
anyone reading it.

It's easier to print it that way, though. Do you split your WinWord
documents page per page?
--
WildHeart'2k4
Jul 21 '05 #18

P: n/a
Stefano "WildHeart" Lanzavecchia <st********@apl.it> wrote:
Do you keep your .cs code files like that? No, I didn't think so! HTML
deserves the same respect and for exactly the same reason, same goes for
anyone reading it.


It's easier to print it that way, though. Do you split your WinWord
documents page per page?


I can always provide a print version which is just the concatenation of
all the other pages - I think that's what most sites do.

--
Jon Skeet - <sk***@pobox.com>
http://www.pobox.com/~skeet
If replying to the group, please do not mail me too
Jul 21 '05 #19

P: n/a
Jon Skeet,

Its some good work u have in there. you have almost covered all the
technical aspect that goes in with handling the thread. I have already give
a print!!.

Having said this, I'll like to add that, I generally like to treat a Thread
as an fully self managed Object rather than a method of an object that is
used by thread(so to say...). OK now I am confusing... what I mean is(more
of a design issue), in every example I see, there are code written like
this:

ThreadStart job = new ThreadStart(myobj.ThreadJob);
Thread thread = new Thread(job);
thread.Start();

well there is nothing wrong with this, but I like to see something like

MyThread thread = new MyThread();

this makes my design lot cleaner to understand, How I do this is something
like this.

class abstract ThreadBase
{
<< events for OnThreadStart, OnThreadDone, OnXXX>>
void abstract Run();
protected virtual void OuterRun()
{
<< trigger OnStartEvent>>
try { Run(); }
catch(...){<<handle Exception & raise events>>}
<< trigger OnThreadDoneEvent>>
}
public ThreadBase()//constructor
{
ThreadStart job = new ThreadStart(OuterRun);
Thread thread = new Thread(job);
thread.Start();
}
}
this way the method that is run in the thread & the thread itself are
treated as from single object. and I have lot of common functionality in the
ThreadBase so that I don't have to re write it for every time I want to use
a thread.

all I have to do for creating a new thread functionality is derive a class
from ThreadBase and override its Run() method and other OnXXX method.

I may be biased here with this approach as I come form Delphi background,
but I have not found much issue with it so far.

Regards
Kiran

Jul 21 '05 #20

P: n/a
Kiran <ki****@abc.com> wrote:
Jon Skeet,

Its some good work u have in there. you have almost covered all the
technical aspect that goes in with handling the thread. I have already give
a print!!.

Having said this, I'll like to add that, I generally like to treat a Thread
as an fully self managed Object rather than a method of an object that is
used by thread(so to say...). OK now I am confusing... what I mean is(more
of a design issue), in every example I see, there are code written like
this:

ThreadStart job = new ThreadStart(myobj.ThreadJob);
Thread thread = new Thread(job);
thread.Start();

well there is nothing wrong with this, but I like to see something like

MyThread thread = new MyThread();

this makes my design lot cleaner to understand, How I do this is something
like this.
<snip>
this way the method that is run in the thread & the thread itself are
treated as from single object.


Whereas I would discourage that approach. It's the one that Java took
until it gained the Runnable interface, and these days most people will
strongly discourage people from subclassing Thread in Java.

I see no advantage in conflating the two ideas, to be honest. There's
nothing wrong with having a general purpose class which can be used to
start a thread and is still provided with a ThreadStart delegate to
actually run, but to restrict things by derivation is a bad idea, in my
view.
--
Jon Skeet - <sk***@pobox.com>
http://www.pobox.com/~skeet
If replying to the group, please do not mail me too
Jul 21 '05 #21

P: n/a
Hi Jon,

I did not go deep in your page code, however what I see often in the
newsgroups that in my opinion people start using multithreading on the wrong
places.

To say it simple, I miss the chapter (or did not see it), "Why using
multithreading and when using multithreading".

The "why" is for me to use processor power when there is a process stopped
because that it needs user or another acknowledge on a process to let it go
on. Another process can than run in the time that waiting is done.

For the when it is for me.
-There has to be more processes, which are independent from a user, or any
other processes running, which can have interrupts because of IO or things
like that (and independent means even not need an end acknowledge going on).
-When this is not the issue however there are more processors and known that
there is only one application running there can be used optimistic
multithreading.

A nice example is printing which is by default because it is done by the OS
already a multithreading process. (For you because you are in my opinion so
on words, that is multiprocessing (or even a better classic word for it
multiprogram) in fact, however when it not has been, it would have been nice
to do it multithreading).

This is only to give you some ideas about this.

Cor
Jul 21 '05 #22

P: n/a
Cor Ligthert <no**********@planet.nl> wrote:
I did not go deep in your page code, however what I see often in the
newsgroups that in my opinion people start using multithreading on the wrong
places.

To say it simple, I miss the chapter (or did not see it), "Why using
multithreading and when using multithreading".
Yes, that wouldn't be a bad idea.
The "why" is for me to use processor power when there is a process stopped
because that it needs user or another acknowledge on a process to let it go
on. Another process can than run in the time that waiting is done.
Not necessarily. It also allows things like a UI updating while doing
something else in the background as well.
For the when it is for me.
-There has to be more processes, which are independent from a user, or any
other processes running, which can have interrupts because of IO or things
like that (and independent means even not need an end acknowledge going on).
They don't need to be indepedent, or use interrupts. It's quite
possible to have a CPU-bound background thread (preferably at a low
priority), and have it update the UI (using Control.Invoke) to give the
end user information about what's happening.
-When this is not the issue however there are more processors and known that
there is only one application running there can be used optimistic
multithreading.
Not sure what you mean here. Could you try rewriting that sentence?
A nice example is printing which is by default because it is done by the OS
already a multithreading process. (For you because you are in my opinion so
on words, that is multiprocessing (or even a better classic word for it
multiprogram) in fact, however when it not has been, it would have been nice
to do it multithreading).


(The disadvantage with doing it in threads rather than using a separate
spooler is that you'd have to keep the application running while you
were printing, of course.)

I already draw a bit of a parallel between multi-tasking and multi-
threading, but I agree that it would be a good idea to write more about
it, with other examples.

--
Jon Skeet - <sk***@pobox.com>
http://www.pobox.com/~skeet
If replying to the group, please do not mail me too
Jul 21 '05 #23

P: n/a
Hi Jon,

And again starts a discussion.
Not necessarily. It also allows things like a UI updating while doing
something else in the background as well.
This gives no advantages only slower UI updating. There is no advantage when
other processors do not take things over, only slower UI, however when other
processors are taking things over (what can be in painting), than it is
again IO and you can do it in more threads.

As far as I know does, although the hyper threading optimizes the
multithreading process, a processor only one thing in a time.
They don't need to be indepedent, or use interrupts. It's quite
possible to have a CPU-bound background thread (preferably at a low
priority), and have it update the UI (using Control.Invoke) to give the
end user information about what's happening.

This is in my opinion only interesting when there is an actif foreground
thread, than it completly as I tried to write.

More processorrs:
What I wrote above is not always true when there are more processors,
however this is not really a multithreading isue.

When there is only one application running, you can use the not used
processors to do processing in an optimistic way. When there is a reason it
can not be used you can garbage it. However do never forget that 2
processors have to be processed.

I hope this gives some more explanations about my thoughts about this?

Cor
Jul 21 '05 #24

P: n/a
Cor Ligthert <no**********@planet.nl> wrote:
And again starts a discussion.
Not necessarily. It also allows things like a UI updating while doing
something else in the background as well.
This gives no advantages only slower UI updating.
Yes it does!

1) You can give the user information as to progress
2) If windows are dragged over the UI, it can repaint rather just
leaving a white rectangle
3) You can allow buttons etc for cancelling the other thread's work,
and the app can be closed in the normal way
4) The user may be able to do other things while the processing is
going on (depending on the app)
There is no advantage when
other processors do not take things over, only slower UI, however when other
processors are taking things over (what can be in painting), than it is
again IO and you can do it in more threads.
The point is that you have a *working* UI. It will be slower than if
there isn't the other process, but it's better than a completely
unresponsive UI.
As far as I know does, although the hyper threading optimizes the
multithreading process, a processor only one thing in a time.
No - hyperthreading makes the processor look like two processors as far
as the OS is concerned, so two threads can run on it at the same time,
using different parts of the processor.
They don't need to be indepedent, or use interrupts. It's quite
possible to have a CPU-bound background thread (preferably at a low
priority), and have it update the UI (using Control.Invoke) to give the
end user information about what's happening.


This is in my opinion only interesting when there is an actif foreground
thread, than it completly as I tried to write.


The UI *is* an active foreground thread. If you want the UI to remain
responsive *at all* then either you need to call Application.DoEvents
(urgh!) or you need another thread.
More processorrs:
What I wrote above is not always true when there are more processors,
however this is not really a multithreading isue.
Well, it's something you need to be aware of when threading - you may
wish to tailor the number of threads you use depending on the number of
processors, in some special cases. (I could mention that in the
article, actually.)
When there is only one application running, you can use the not used
processors to do processing in an optimistic way. When there is a reason it
can not be used you can garbage it. However do never forget that 2
processors have to be processed.

I hope this gives some more explanations about my thoughts about this?


Yes, but I disagree very strongly. It would be easy to come up with a
couple of example programs, one of which uses threading and one of
which doesn't, if you're still not convinced.

--
Jon Skeet - <sk***@pobox.com>
http://www.pobox.com/~skeet
If replying to the group, please do not mail me too
Jul 21 '05 #25

P: n/a
Jon,

Please no constructive quoting otherwise we can better stop this, I am sure
you do it not express, however read first the complete message and add than
your comments?
Not necessarily. It also allows things like a UI updating while doing
something else in the background as well.
This gives no advantages only slower UI updating.
Yes it does!

1) You can give the user information as to progress
2) If windows are dragged over the UI, it can repaint rather just
leaving a white rectangle
3) You can allow buttons etc for cancelling the other thread's work,
and the app can be closed in the normal way
4) The user may be able to do other things while the processing is
going on (depending on the app)


I wrote 4. The others can be done in another way while there is than a
higher throughput, I get very much the idea that you forget that the OS has
to process the threads, which is not measurable inside a program.
There is no advantage when
other processors do not take things over, only slower UI, however when other processors are taking things over (what can be in painting), than it is
again IO and you can do it in more threads.
The point is that you have a *working* UI. It will be slower than if
there isn't the other process, but it's better than a completely
unresponsive UI.

See my previous sentence.
As far as I know does, although the hyper threading optimizes the
multithreading process, a processor only one thing in a time.


No - hyperthreading makes the processor look like two processors as far
as the OS is concerned, so two threads can run on it at the same time,
using different parts of the processor.


You write, "No", what is the difference with what I wrote? You write "run"
which is true, I wrote "process" in my opinion are that two different
things.
They don't need to be indepedent, or use interrupts. It's quite
possible to have a CPU-bound background thread (preferably at a low
priority), and have it update the UI (using Control.Invoke) to give the end user information about what's happening.
This is in my opinion only interesting when there is an actif foreground thread, than it completly as I tried to write.


The UI *is* an active foreground thread. If you want the UI to remain
responsive *at all* then either you need to call Application.DoEvents
(urgh!) or you need another thread.


see my previous sentence
More processorrs:
What I wrote above is not always true when there are more processors,
however this is not really a multithreading isue.


Well, it's something you need to be aware of when threading - you may
wish to tailor the number of threads you use depending on the number of
processors, in some special cases. (I could mention that in the
article, actually.)
When there is only one application running, you can use the not used
processors to do processing in an optimistic way. When there is a reason it can not be used you can garbage it. However do never forget that 2
processors have to be processed.

I hope this gives some more explanations about my thoughts about this?


Yes, but I disagree very strongly. It would be easy to come up with a
couple of example programs, one of which uses threading and one of
which doesn't, if you're still not convinced.

I do use multithreading and there are a lot of advantages, however I have
seen that it is used in some circumstances not needed. Processing
multithreading takes time, so it can have a negative influence on the
througput of the program.
Cor

Jul 21 '05 #26

P: n/a
Cor Ligthert <no**********@planet.nl> wrote:
Please no constructive quoting otherwise we can better stop this, I am sure
you do it not express, however read first the complete message and add than
your comments?
I really don't know what you mean. I haven't done any creative snipping
or anything here...
> Not necessarily. It also allows things like a UI updating while doing
> something else in the background as well.

This gives no advantages only slower UI updating.


Yes it does!

1) You can give the user information as to progress
2) If windows are dragged over the UI, it can repaint rather just
leaving a white rectangle
3) You can allow buttons etc for cancelling the other thread's work,
and the app can be closed in the normal way
4) The user may be able to do other things while the processing is
going on (depending on the app)


I wrote 4.


Where, exactly? Maybe it was in one of the sentences where I didn't
fully understand your meaning.
The others can be done in another way while there is than a
higher throughput, I get very much the idea that you forget that the OS has
to process the threads, which is not measurable inside a program.


No, the others are *definitely* better done using threads. How else
would you do it? Using Application.DoEvents()? That leads to horrible
re-entrancy problems, in my opinion, and is really a hack dating back
to a time when threads weren't easily available. It also involves
guesswork as to where is a good place to call DoEvents - the more often
you call it, the more throughput you lose, but the less often you call
it, the less responsive the UI is.
There is no advantage when
other processors do not take things over, only slower UI, however when other processors are taking things over (what can be in painting), than it is
again IO and you can do it in more threads.


The point is that you have a *working* UI. It will be slower than if
there isn't the other process, but it's better than a completely
unresponsive UI.

See my previous sentence.


You've never said how you'd achieve a working GUI, as far as I've seen.
As far as I know does, although the hyper threading optimizes the
multithreading process, a processor only one thing in a time.


No - hyperthreading makes the processor look like two processors as far
as the OS is concerned, so two threads can run on it at the same time,
using different parts of the processor.


You write, "No", what is the difference with what I wrote? You write "run"
which is true, I wrote "process" in my opinion are that two different
things.


You didn't actually write any verb in that clause - you just wrote "a
processor only one thing at a time". However, a hyperthreading
processor really *can* be processing two threads at a time. For
instance, an instruction for one thread might be being decoded while
the instruction for another thread runs in the floating point unit.
Given the deep pipelining in many processors, this is only a more
complicated version of what's been happening for ages, with many
instructions being processed at the same time anyway. It's just that in
the past, those instructions all had to be from the same thread.
> They don't need to be indepedent, or use interrupts. It's quite
> possible to have a CPU-bound background thread (preferably at a low
> priority), and have it update the UI (using Control.Invoke) to give the > end user information about what's happening.

This is in my opinion only interesting when there is an actif foreground thread, than it completly as I tried to write.


The UI *is* an active foreground thread. If you want the UI to remain
responsive *at all* then either you need to call Application.DoEvents
(urgh!) or you need another thread.


see my previous sentence


I really don't see how that covers it. Or are you advocating UIs which
are unresponsive? Note that "unresponsive" doesn't just mean that the
user needs to be able to do other things - an app which doesn't even
repaint is unresponsive, in my view.

<snip>
Yes, but I disagree very strongly. It would be easy to come up with a
couple of example programs, one of which uses threading and one of
which doesn't, if you're still not convinced.

I do use multithreading and there are a lot of advantages, however I have
seen that it is used in some circumstances not needed. Processing
multithreading takes time, so it can have a negative influence on the
througput of the program.


It certainly can - but not in the examples presented here. Or rather,
the throughput *will* be very slightly diminished, but at the gain of a
*much* better user experience. I would rather have an interface which
took 4 hours to do something but kept me informed of what was going on,
let me cancel it, etc than an app which took 3 hours and 55 minutes but
never bothered to repaint itself.

--
Jon Skeet - <sk***@pobox.com>
http://www.pobox.com/~skeet
If replying to the group, please do not mail me too
Jul 21 '05 #27

P: n/a
Hi Jon,

I stop with this thread, I wrote you why I did it and was not interested
starting a discussion about this.

The differences are so thin, that it makes no sense the only thing I would
bring to you is that a thread is not forever the solution as I often get the
idea that some people are thinking, it can be a sollution and you have to
think twice before you use it.

I gave you some samples to help you to add to your page, when you do not
agree that with me is for me no problem, do it your way, it are your pages.

Do not think I am angry or something, I am not at all.

However this are parts where in my opinion I can be right and where you can
be right depending on the circumstances, so discussing using a newsgroup
makes it to difficult.

Cor
Jul 21 '05 #28

P: n/a
Cor Ligthert <no**********@planet.nl> wrote:

<snip>
However this are parts where in my opinion I can be right and where you can
be right depending on the circumstances, so discussing using a newsgroup
makes it to difficult.


If you say so. I don't think it's *ever* right (in anything but a
throwaway test program) to perform long-running operations in the UI
thread, unless they absolutely *have* to due to them making a lot of
changes to the UI controls themselves. Almost anything not directly
related to the UI shouldn't occur on the UI thread, in my view. I think
you'll find most people would agree with me on that, too.

I'll certainly add something to the page about some times when it's
*not* worth creating lots of threads, or when you create a thread but
then don't use the fact that you've created it (e.g. by calling
BeginInvoke and then calling EndInvoke on the next line, which
effectively makes it synchronous anyway).

--
Jon Skeet - <sk***@pobox.com>
http://www.pobox.com/~skeet
If replying to the group, please do not mail me too
Jul 21 '05 #29

P: n/a

"Chris Mullins" <cm******@yahoo.com> wrote in message
news:%2****************@TK2MSFTNGP10.phx.gbl...
I can't work out whether I'm surprised that VB.NET lacks volatile or
not - I think I *may* have tried to find an equivalent and failed, but
I'm not sure.


I've looked several times for it, and each time been quite shocked not
to find it. I always feel if I dig "just a bit deeper" I'll find it. Ah
well.
One more thing that occasionally forces me to write code in C#...


See Thread.VolatileRead and Thread.VolatileWrite.
Jul 21 '05 #30

P: n/a
This is not surprising at all. The distinction between Monitor and Mutex in
..Net is the same as the distinction between critical sections and mutexes in
Win32. The critical section will in many cases completely bypass the
underlying lock. If you have multiple processors, you increase concurrency
and therefore lock contention, forcing the crit sect to actually enter its
underlying lock more often.

"Daniel O'Connell [C# MVP]" <onyxkirx@--NOSPAM--comcast.net> wrote in
message news:u5**************@tk2msftngp13.phx.gbl...
Results (3 tests each):

Using a lock:
Count per second: 28038249.614792
Count per second: 28113891.7558685
Count per second: 27467033.4397496

Using a manual reset event:
Count per second: 1087057.6
Count per second: 1086289.82785602
Count per second: 1082087.3

So on average, using a lock we're able to count about 25 times as fast.

Just another example of threading not being a good thing to guess about
:)

I'd be very interested to see whether you get the same kind of results
- it may *very* well depend to a very large extent on the CPU(s) of the
test machines. Mine is a P4/3.06GHz laptop; no hyperthreading or
anything like that. I'd be particularly interested to know the numbers
on a dual processor machine...
The numbers are a bit better on my dual processor machine, but not
substantially. Looks like lock is about 10x faster on this machine.
I don't think that these results would be sufficent to state that the
differences are due to multiple processor machines so much as difference

in speeds between the processors I used and the one you used. I ran this on a
dual 2.2 ghz xeon, with hyperthreading. I was able to verify that the speed of locks and events are as identical as benchmarks can be when affinity
targets one or two physical processors, or all four virtual processors,
however. I would guess that the speed of these sync mechanisms are pretty
much neutral to the number of processors as long as their is only one
thread. A more interesting, but more complicated, test would be to test lock and event speed across multiple threads.

Using a lock:
Count per second: 5581104.27543036
Count per second: 5615276.9
Count per second: 5500714.86697966

Using a manual reset event:
Count per second: 500654.6
Count per second: 489649.3771518
Count per second: 494306.7

Jul 21 '05 #31

P: n/a
You might consider including some discussion of ReaderWriterLock -- kind of
an advanced feature, but could be useful in certain scenarios.

"Jon Skeet [C# MVP]" <sk***@pobox.com> wrote in message
news:MP************************@msnews.microsoft.c om...
Please excuse the cross-post - I'm pretty sure I've had interest in the
article on all the groups this is posted to.

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.

The article is at:
http://www.pobox.com/~skeet/csharp/multithreading.html

--
Jon Skeet - <sk***@pobox.com>
http://www.pobox.com/~skeet
If replying to the group, please do not mail me too

Jul 21 '05 #32

P: n/a
> This is not surprising at all. The distinction between Monitor and Mutex
in
.Net is the same as the distinction between critical sections and mutexes in Win32. The critical section will in many cases completely bypass the
underlying lock. If you have multiple processors, you increase concurrency
and therefore lock contention, forcing the crit sect to actually enter its
underlying lock more often.


FWIW, here is a simple critical section in c#. A lot more state could be
added, but this is basic.

using System;
using System.Threading;

namespace SyncPrimitives
{
/// <summary>
/// Simple CriticalSection Implementation in c#.
/// Note we don't use a "busy wait" but a test and sleep method.
/// The current thread gives up it cpu slice to allow another thread to
run.
/// The idea is that owner thread will run and release the lock and we will
get
/// the lock when we wake and test again. This should be the normal
behavior
/// if critical sections are only used to protect very short sections
/// of code. Two threads will only rarely attempt to enter the critical
section
/// at the same time, and even then, the loser will only have to spin/sleep
for
/// a brief time. We could also upgrade to waiting on an event, but that
would be
/// another version and we only want to show simple implementation. We
could
/// also add thead "state" so that only owner can Exit and owner can
reenter lock.
/// </summary>
public sealed class CriticalSection
{
private int spinLock = 0; // Not locked.
public CriticalSection()
{
}
/// <summary>
/// Enter the CriticalSection exclusively or wait until we do.
/// A CriticalSection is intended to be Entered and Exited quickly.
/// </summary>
public void Enter()
{
while( Interlocked.Exchange(ref spinLock, 1) == 1 )
Thread.Sleep(0);
// We got the lock as it was 0 (unlocked) before Exchange.
}
public bool Enter(int milliseconds)
{
if ( Interlocked.Exchange(ref spinLock, 1) == 0 )
return true;
Thread.Sleep(milliseconds);
if ( Interlocked.Exchange(ref spinLock, 1) == 0 )
return true;
return false;
}
/// <summary>
/// Non-blocking version of Enter method.
/// </summary>
/// <returns></returns>
public bool TryEnter()
{
if ( Interlocked.Exchange(ref spinLock, 1) == 0 )
return true;
return false;
}
/// <summary>
/// Release the lock. Note that any thread could call Exit with this
/// implementation, which resembles Semaphore Release behavior.
/// </summary>
public void Exit()
{
Interlocked.Exchange(ref spinLock, 0);
}
public bool IsLocked()
{
if ( Interlocked.CompareExchange(ref spinLock, 0, 0) == 0 )
return false;
return true;
}
}
}

Jul 21 '05 #33

P: n/a
Ted Miller <te*@nwlink.com> wrote:
You might consider including some discussion of ReaderWriterLock -- kind of
an advanced feature, but could be useful in certain scenarios.


Yup, it's on my wishlist.

--
Jon Skeet - <sk***@pobox.com>
http://www.pobox.com/~skeet
If replying to the group, please do not mail me too
Jul 21 '05 #34

P: n/a
Hi,
Spinlock could be fine if it is held only occasionally (i.e. once in a
while) and only for a few loops. Otherwise it is quite bad for laptop
battery power. Nowadays processor architectures/technologies such as Intel's
SpeedStep/Clock Modulation, AMD's PowerNow and Transmeta Crusoe Longrun
support dropping of processor's clock frequency based on processor's usage
pattern. Ie. if processor finds that it uses lots of time in idle, then it
is free to drop frequency for saving battery power (both less power
consumption by processor itself and better thermal control - i.e. less power
to fans). Spinlock is actually an attempt to simulate thread's idle wait but
from processor point of view it is indistinguishable from any other
processing intensive operations (definitely not an idle). Every spinlock
resets processor's last contiguous idle time slice and reduces overall
weight of idle time. Frequent use of spinlocks could cause that processor
will be always running on high frequency without any reasons, burning
battery power just for running dumb spins and fans that has to work double
for cooling overloaded processor. If someone sits on the plain with the
laptop and run program that relies on spinlocks - he/she would not really
appreciate what this program does for his/her batteries (not speaking of
extreme circumstances when people burns genitalia by their laptops see:
http://www.wired.com/news/business/0...56552,00.html).
-Valery
http://www.harper.no/valery
"William Stacey [MVP]" <st***********@mvps.org> wrote in message
news:%2****************@TK2MSFTNGP12.phx.gbl...
This is not surprising at all. The distinction between Monitor and Mutex

in
.Net is the same as the distinction between critical sections and mutexes

in
Win32. The critical section will in many cases completely bypass the
underlying lock. If you have multiple processors, you increase
concurrency
and therefore lock contention, forcing the crit sect to actually enter
its
underlying lock more often.


FWIW, here is a simple critical section in c#. A lot more state could be
added, but this is basic.

using System;
using System.Threading;

namespace SyncPrimitives
{
/// <summary>
/// Simple CriticalSection Implementation in c#.
/// Note we don't use a "busy wait" but a test and sleep method.
/// The current thread gives up it cpu slice to allow another thread to
run.
/// The idea is that owner thread will run and release the lock and we
will
get
/// the lock when we wake and test again. This should be the normal
behavior
/// if critical sections are only used to protect very short sections
/// of code. Two threads will only rarely attempt to enter the critical
section
/// at the same time, and even then, the loser will only have to
spin/sleep
for
/// a brief time. We could also upgrade to waiting on an event, but that
would be
/// another version and we only want to show simple implementation. We
could
/// also add thead "state" so that only owner can Exit and owner can
reenter lock.
/// </summary>
public sealed class CriticalSection
{
private int spinLock = 0; // Not locked.
public CriticalSection()
{
}
/// <summary>
/// Enter the CriticalSection exclusively or wait until we do.
/// A CriticalSection is intended to be Entered and Exited quickly.
/// </summary>
public void Enter()
{
while( Interlocked.Exchange(ref spinLock, 1) == 1 )
Thread.Sleep(0);
// We got the lock as it was 0 (unlocked) before Exchange.
}
public bool Enter(int milliseconds)
{
if ( Interlocked.Exchange(ref spinLock, 1) == 0 )
return true;
Thread.Sleep(milliseconds);
if ( Interlocked.Exchange(ref spinLock, 1) == 0 )
return true;
return false;
}
/// <summary>
/// Non-blocking version of Enter method.
/// </summary>
/// <returns></returns>
public bool TryEnter()
{
if ( Interlocked.Exchange(ref spinLock, 1) == 0 )
return true;
return false;
}
/// <summary>
/// Release the lock. Note that any thread could call Exit with this
/// implementation, which resembles Semaphore Release behavior.
/// </summary>
public void Exit()
{
Interlocked.Exchange(ref spinLock, 0);
}
public bool IsLocked()
{
if ( Interlocked.CompareExchange(ref spinLock, 0, 0) == 0 )
return false;
return true;
}
}
}

Jul 21 '05 #35

P: n/a
Hi Jon,

Not about the rest of this thread, only about your answer and not agreeing
with you all the rest you stated, however to give you "one" sample to think
about.

Almost anything not directly
related to the UI shouldn't occur on the UI thread, in my view. I think
you'll find most people would agree with me on that, too.


Every process shorter than the human reaction time of the client is useless
to place in an extra thread.

Cor
Jul 21 '05 #36

P: n/a
Cor Ligthert <no**********@planet.nl> wrote:
Not about the rest of this thread, only about your answer and not agreeing
with you all the rest you stated, however to give you "one" sample to think
about.

Almost anything not directly
related to the UI shouldn't occur on the UI thread, in my view. I think
you'll find most people would agree with me on that, too.


Every process shorter than the human reaction time of the client is useless
to place in an extra thread.


Yes, I'd agree with you on that one, if you can really guarantee it'll
take that short a time.

--
Jon Skeet - <sk***@pobox.com>
http://www.pobox.com/~skeet
If replying to the group, please do not mail me too
Jul 21 '05 #37

P: n/a
> Spinlock could be fine if it is held only occasionally (i.e. once in a
while) and only for a few loops.
Correct. I think I pointed that out in the code.
to fans). Spinlock is actually an attempt to simulate thread's idle wait but from processor point of view it is indistinguishable from any other
processing intensive operations (definitely not an idle). Every spinlock
Note this is not a busy-wait. The spin sleeps or gives up it slice so the
owner can free it. If the owner is busy, the cpu is working anyway. If the
owner is done, the next thread gets the lock, does its thing, and exits the
lock. This issue can arise if the owner blocks for long time while owning
the lock. That is not good and another lock should be used. It should only
be protecting a few lines in most cases. AFIK, every primitive is
ultimately based on a spin lock at the lowest level. So spins are mostly
used for creating higher sync primitives. If this is not the case, please
inform.
If someone sits on the plain with the
laptop and run program that relies on spinlocks - he/she would not really
appreciate what this program does for his/her batteries (not speaking of
extreme circumstances when people burns genitalia by their laptops see:

http://www.wired.com/news/business/0...56552,00.html).

Actually that kinda happened to me once, so I feel the pain :-) Cheers.

--
William Stacey, MVP

Jul 21 '05 #38

P: n/a
Here is another version I did that goes right to an event wait if lock is
locked. Please review for errors and comments as this is a bit tricky. You
could cheat and use a Monitor, but that would not be very interesting.
Cheers!

using System;
using System.Threading;

namespace SyncPrimitives
{
/// <summary>
/// Simple CriticalSection Implementation in c#.
/// Note we don't use a "busy wait" but a test and wait.
/// This version uses event wait if lock is owned and keeps state of
current owner.
/// </summary>
public sealed class CriticalSection
{
private volatile Thread owner = null;
private int lockCount = 0;
private readonly AutoResetEvent lockedEvent = new AutoResetEvent(false);
private const int maxSpinCount = 400;

public CriticalSection()
{
}

public bool Enter(int milliseconds)
{
DateTime startTime;
int spinCount = 0;
//Block while locked (unless we are the owner).
while( Interlocked.CompareExchange(ref lockCount, 1, 0) > 0 )
{
// It is locked, so check if we are the owner.
if ( Object.ReferenceEquals(owner, Thread.CurrentThread) )
{
// We already own the lock, so just increment count.
lockCount++;
return true;
}

// Note if we get to this point, we are not the owner.
if ( milliseconds == 0 )
return false;

if ( spinCount < maxSpinCount )
{
spinCount++;
Thread.Sleep(0); // Don't WaitOne() until we have tried
maxSpinCount Times.
continue; // The idea is we will get the lock very
quickly and don't want overhead of wait in the fast path.
}

startTime = DateTime.Now;
try
{
if ( ! lockedEvent.WaitOne(milliseconds, false) )
return false; // Did not get a signal within timeout.
}
catch
{
lockedEvent.Set(); // Could be got signalled before the exception, so
signal next thread.
throw;
}
milliseconds = (int)(milliseconds - ((TimeSpan)(DateTime.Now -
startTime)).TotalMilliseconds);
if ( milliseconds < 0 )
milliseconds = 0;
}
// We got the lock and set to 1 as prev lockCount was zero.
owner = Thread.CurrentThread;
return true;
}

public bool TryEnter()
{
return Enter(0);
}

public void Exit()
{
if ( Interlocked.CompareExchange(ref lockCount, 0, 0) == 0 )
throw new SynchronizationLockException("Can not exit lock that has not
been entered.");

// It is locked. See if we are the owner. If not, throw exception.
if ( ! Object.ReferenceEquals(owner, Thread.CurrentThread) )
throw new SynchronizationLockException("Not current owner of lock.");

// We are the owner, so decrement count.
lockCount--;
if ( lockCount == 0 )
{
owner = null;
lockedEvent.Set();
}
}

public bool IsLocked()
{
if ( Interlocked.CompareExchange(ref lockCount, 0, 0) == 0 )
return false;
return true;
}
}
}
--
William Stacey, MVP

Jul 21 '05 #39

P: n/a
Interesting point Valery.

It's hard to write spinlock at the level of abstraction we are at in
..NET. Ideally the JIT compiler could choose a spinlock implementation
based on the environment. On a single processor machine the spinning
is a waste of processor time, and on a laptop the spinlock causes
additional power consumption - both good reasons to switch to a
different implementation in these environments.

--
Scott
http://www.OdeToCode.com
On Mon, 21 Jun 2004 09:27:15 +0200, "Valery Pryamikov"
<Va****@nospam.harper.no> wrote:
Hi,
Spinlock could be fine if it is held only occasionally (i.e. once in a
while) and only for a few loops. Otherwise it is quite bad for laptop
battery power. Nowadays processor architectures/technologies such as Intel's
SpeedStep/Clock Modulation, AMD's PowerNow and Transmeta Crusoe Longrun
support dropping of processor's clock frequency based on processor's usage
pattern. Ie. if processor finds that it uses lots of time in idle, then it
is free to drop frequency for saving battery power (both less power
consumption by processor itself and better thermal control - i.e. less power
to fans). Spinlock is actually an attempt to simulate thread's idle wait but
from processor point of view it is indistinguishable from any other
processing intensive operations (definitely not an idle). Every spinlock
resets processor's last contiguous idle time slice and reduces overall
weight of idle time. Frequent use of spinlocks could cause that processor
will be always running on high frequency without any reasons, burning
battery power just for running dumb spins and fans that has to work double
for cooling overloaded processor. If someone sits on the plain with the
laptop and run program that relies on spinlocks - he/she would not really
appreciate what this program does for his/her batteries (not speaking of
extreme circumstances when people burns genitalia by their laptops see:
http://www.wired.com/news/business/0...56552,00.html).
-Valery
http://www.harper.no/valery


Jul 21 '05 #40

P: n/a
> AFIK, every primitive is
ultimately based on a spin lock at the lowest level.

If we talk about kernel mode Win32 synchronization objects - than it's not
quite so.
Yes, spin lock is used by code, but it is different in two ways:
- When thread scheduler sees that thread is waiting on Win32 synchronization
objects it immediately schedules context switch to another runnable thread
instead of wasting time spinning.

- Additionally, kernel mode spin lock sets idle bit on interrupt command
register (on Intel) or use other means (on other processor types) to notify
processor that thread is running idle operation. Interrupt command register
and this bit is not accessible from the user mode.
-Valery.
http://www.harper.no/valery
"William Stacey [MVP]" <st***********@mvps.org> wrote in message
news:%2****************@TK2MSFTNGP12.phx.gbl...
Spinlock could be fine if it is held only occasionally (i.e. once in a
while) and only for a few loops.


Correct. I think I pointed that out in the code.
to fans). Spinlock is actually an attempt to simulate thread's idle wait

but
from processor point of view it is indistinguishable from any other
processing intensive operations (definitely not an idle). Every spinlock


Note this is not a busy-wait. The spin sleeps or gives up it slice so the
owner can free it. If the owner is busy, the cpu is working anyway. If
the
owner is done, the next thread gets the lock, does its thing, and exits
the
lock. This issue can arise if the owner blocks for long time while owning
the lock. That is not good and another lock should be used. It should
only
be protecting a few lines in most cases. AFIK, every primitive is
ultimately based on a spin lock at the lowest level. So spins are mostly
used for creating higher sync primitives. If this is not the case, please
inform.
If someone sits on the plain with the
laptop and run program that relies on spinlocks - he/she would not really
appreciate what this program does for his/her batteries (not speaking of
extreme circumstances when people burns genitalia by their laptops see:

http://www.wired.com/news/business/0...56552,00.html).

Actually that kinda happened to me once, so I feel the pain :-) Cheers.

--
William Stacey, MVP

Jul 21 '05 #41

P: n/a
It's best to rely on Win32 synchronization objects, because they know how to
handle idle operations.
For example: when thread scheduler sees that thread is waiting on Win32
synchronization objects it immediately schedules context switch to another
runnable thread instead of wasting time spinning.

Additionally, kernel mode spin lock sets idle bit on interrupt command
register (on Intel) or use other means (on different processor types) to
notify processor that thread is running idle operation. Interrupt command
register and this bit is not accessible from the user mode.

-Valery.

http://www.harper.no/valery

"Scott Allen" <bitmask@[nospam].fred.net> wrote in message
news:76********************************@4ax.com...
Interesting point Valery.

It's hard to write spinlock at the level of abstraction we are at in
.NET. Ideally the JIT compiler could choose a spinlock implementation
based on the environment. On a single processor machine the spinning
is a waste of processor time, and on a laptop the spinlock causes
additional power consumption - both good reasons to switch to a
different implementation in these environments.

--
Scott
http://www.OdeToCode.com
On Mon, 21 Jun 2004 09:27:15 +0200, "Valery Pryamikov"
<Va****@nospam.harper.no> wrote:
Hi,
Spinlock could be fine if it is held only occasionally (i.e. once in a
while) and only for a few loops. Otherwise it is quite bad for laptop
battery power. Nowadays processor architectures/technologies such as
Intel's
SpeedStep/Clock Modulation, AMD's PowerNow and Transmeta Crusoe Longrun
support dropping of processor's clock frequency based on processor's usage
pattern. Ie. if processor finds that it uses lots of time in idle, then it
is free to drop frequency for saving battery power (both less power
consumption by processor itself and better thermal control - i.e. less
power
to fans). Spinlock is actually an attempt to simulate thread's idle wait
but
from processor point of view it is indistinguishable from any other
processing intensive operations (definitely not an idle). Every spinlock
resets processor's last contiguous idle time slice and reduces overall
weight of idle time. Frequent use of spinlocks could cause that processor
will be always running on high frequency without any reasons, burning
battery power just for running dumb spins and fans that has to work double
for cooling overloaded processor. If someone sits on the plain with the
laptop and run program that relies on spinlocks - he/she would not really
appreciate what this program does for his/her batteries (not speaking of
extreme circumstances when people burns genitalia by their laptops see:
http://www.wired.com/news/business/0...56552,00.html).
-Valery
http://www.harper.no/valery

Jul 21 '05 #42

P: n/a
My second implementation uses system autoresetevent for the wait, so this
should not be an issue.

--
William Stacey, MVP

"Valery Pryamikov" <Va****@nospam.harper.no> wrote in message
news:#s**************@TK2MSFTNGP10.phx.gbl...
It's best to rely on Win32 synchronization objects, because they know how to handle idle operations.
For example: when thread scheduler sees that thread is waiting on Win32
synchronization objects it immediately schedules context switch to another
runnable thread instead of wasting time spinning.

Additionally, kernel mode spin lock sets idle bit on interrupt command
register (on Intel) or use other means (on different processor types) to
notify processor that thread is running idle operation. Interrupt command
register and this bit is not accessible from the user mode.

-Valery.

http://www.harper.no/valery

"Scott Allen" <bitmask@[nospam].fred.net> wrote in message
news:76********************************@4ax.com...
Interesting point Valery.

It's hard to write spinlock at the level of abstraction we are at in
.NET. Ideally the JIT compiler could choose a spinlock implementation
based on the environment. On a single processor machine the spinning
is a waste of processor time, and on a laptop the spinlock causes
additional power consumption - both good reasons to switch to a
different implementation in these environments.

--
Scott
http://www.OdeToCode.com
On Mon, 21 Jun 2004 09:27:15 +0200, "Valery Pryamikov"
<Va****@nospam.harper.no> wrote:
Hi,
Spinlock could be fine if it is held only occasionally (i.e. once in a
while) and only for a few loops. Otherwise it is quite bad for laptop
battery power. Nowadays processor architectures/technologies such as
Intel's
SpeedStep/Clock Modulation, AMD's PowerNow and Transmeta Crusoe Longrun
support dropping of processor's clock frequency based on processor's usagepattern. Ie. if processor finds that it uses lots of time in idle, then itis free to drop frequency for saving battery power (both less power
consumption by processor itself and better thermal control - i.e. less
power
to fans). Spinlock is actually an attempt to simulate thread's idle wait
but
from processor point of view it is indistinguishable from any other
processing intensive operations (definitely not an idle). Every spinlock
resets processor's last contiguous idle time slice and reduces overall
weight of idle time. Frequent use of spinlocks could cause that processorwill be always running on high frequency without any reasons, burning
battery power just for running dumb spins and fans that has to work doublefor cooling overloaded processor. If someone sits on the plain with the
laptop and run program that relies on spinlocks - he/she would not reallyappreciate what this program does for his/her batteries (not speaking of
extreme circumstances when people burns genitalia by their laptops see:
http://www.wired.com/news/business/0...56552,00.html).
-Valery
http://www.harper.no/valery



Jul 21 '05 #43

P: n/a
> On a single processor machine the spinning
is a waste of processor time,


Not if you do a wait or sleep(0) it is not. That gives the other thread a
chance to give up the lock, is which is exactly what you want.

--
William Stacey, MVP
Jul 21 '05 #44

P: n/a
Thanks. You have a c implementation example handy?

--
William Stacey, MVP

"Valery Pryamikov" <Va****@nospam.harper.no> wrote in message
news:up**************@TK2MSFTNGP09.phx.gbl...
AFIK, every primitive is
ultimately based on a spin lock at the lowest level. If we talk about kernel mode Win32 synchronization objects - than it's not
quite so.
Yes, spin lock is used by code, but it is different in two ways:
- When thread scheduler sees that thread is waiting on Win32

synchronization objects it immediately schedules context switch to another runnable thread
instead of wasting time spinning.

- Additionally, kernel mode spin lock sets idle bit on interrupt command
register (on Intel) or use other means (on other processor types) to notify processor that thread is running idle operation. Interrupt command register and this bit is not accessible from the user mode.
-Valery.
http://www.harper.no/valery
"William Stacey [MVP]" <st***********@mvps.org> wrote in message
news:%2****************@TK2MSFTNGP12.phx.gbl...
Spinlock could be fine if it is held only occasionally (i.e. once in a
while) and only for a few loops.


Correct. I think I pointed that out in the code.
to fans). Spinlock is actually an attempt to simulate thread's idle wait
but
from processor point of view it is indistinguishable from any other
processing intensive operations (definitely not an idle). Every
spinlock
Note this is not a busy-wait. The spin sleeps or gives up it slice so the owner can free it. If the owner is busy, the cpu is working anyway. If
the
owner is done, the next thread gets the lock, does its thing, and exits
the
lock. This issue can arise if the owner blocks for long time while owning the lock. That is not good and another lock should be used. It should
only
be protecting a few lines in most cases. AFIK, every primitive is
ultimately based on a spin lock at the lowest level. So spins are mostly used for creating higher sync primitives. If this is not the case, please inform.
If someone sits on the plain with the
laptop and run program that relies on spinlocks - he/she would not

really appreciate what this program does for his/her batteries (not speaking of extreme circumstances when people burns genitalia by their laptops see:

http://www.wired.com/news/business/0...56552,00.html).

Actually that kinda happened to me once, so I feel the pain :-) Cheers.

--
William Stacey, MVP



Jul 21 '05 #45

P: n/a
On Mon, 21 Jun 2004 23:04:45 -0400, "William Stacey [MVP]"
<st***********@mvps.org> wrote:
On a single processor machine the spinning
is a waste of processor time,


Not if you do a wait or sleep(0) it is not. That gives the other thread a
chance to give up the lock, is which is exactly what you want.


I guess it depends on your definition of spinlock.

If spinning means "do not preempt me because I know the lock will be
free any nanosecond now", then spinlocks are about locking CPUs, not
threads, and there wouldn't be any point in chewing up cycles on a
uni-proc system.

If spinning means "try once then yield and repeat", then I'm not sure
what benefit there is actually. I'd have to test this one out quite a
bit to be comfortable with it!

--
Scott
--
Scott
http://www.OdeToCode.com
Jul 21 '05 #46

P: n/a
> If spinning means "do not preempt me because I know the lock will be
free any nanosecond now", then spinlocks are about locking CPUs, not
threads, and there wouldn't be any point in chewing up cycles on a
uni-proc system.
Agreed. On a single cpu, there is no benefit in a *busy-wait as you will
spin for rest of slice as you can't regain the lock anyway until you give up
your slice. Event on a mult-proc, I would not use busy-wait but would still
sleep unless you are tuning for something very specific.
If spinning means "try once then yield and repeat", then I'm not sure
what benefit there is actually. I'd have to test this one out quite a
bit to be comfortable with it!


Benefit is the idea that in general, the lock will be held for only a few
instruction, so do upgrade to monitor or event until you try X times
(sleeping each time to give other thread a chance to release the lock.) In
the general case, there there is lock contention, then you should get at
very next cycle. If you spin/sleep X times, then owner must be blocked on
long operation so upgrade to waiting on a monitor or event. Please test and
reply if bug or error. More of a thing to learn from as using a Monitor is
probably better in most cases.
--
William Stacey, MVP
Jul 21 '05 #47

P: n/a
TT (Tom Tempelaere) <"=?Utf-8?B?VFQgKFRvbSBUZW1wZWxhZXJlKQ==?=" <_|\|_0
$P@|/\|titi____AThotmail.com|/\|@P$0_|\|_>> wrote:
If you are going to write an article on threading, events are a must
(ManualResetEvent and AutoResetEvent). There are situations in which
these more natural to use and more to the point. Plus if you don't
use events, you can't wait on multiple objects (unless you use Mutex
directly instead of Monitor or lock).
Yup - I'll include them when I have some time. (That won't be for a
while - I'm moving house next week.)
If I wouldn't have these event classes and the WaitHandle.WaitAny or
WaitHandle.WaitAll functions, I would have a lot of trouble
implementing the kernel I am now writing. Without the event classes
it would at least be a lot more complex to write (IMHO) and pbbl be
less efficient (although you can't just say that, one should test
that).


They're certainly useful, but I don't think they should usually be used
when simpler constructs (like Monitor) do just as well. Go for the
simplest option, I reckon :)

--
Jon Skeet - <sk***@pobox.com>
http://www.pobox.com/~skeet
If replying to the group, please do not mail me too
Jul 21 '05 #48

P: n/a
TT (Tom Tempelaere) <"=?Utf-8?B?VFQgKFRvbSBUZW1wZWxhZXJlKQ==?=" <_|\|_0
$P@|/\|titi____AThotmail.com|/\|@P$0_|\|_>> wrote:
When I declare a locking object, I always make it readonly (or const
if static) to highlight the fact that the object it refers to will
not change.
Now why didn't I think of that? Excellent idea.
In the producer-consumer class, you could just lock on the queue as
well. Saves one object from memory. The queue object is an internal
state object and no reference to it is passed out of the
producer-consumer context (ie private).
But you don't know for sure whether something within Queue locks on
"this" itself. While that wouldn't be disastrous in this case, I think
it's a simpler policy to never lock on anything that isn't either
private to the class itself and used for nothing but locking, or
something which is explicitly exposed for locking purposes.
You could highlight the fact that Monitor.Enter and Montor.Exit
introduce memory barriers. You mention it only after explaining
Monitor wrt stale data (As of .NET 1.1 there is another way to
introduce a memory barrier ...).
I think that's the right place to mention it though - if I talked about
memory barriers before talking about what they really are, it could get
very confusing. Maybe I could mention them with a link to the
volatility section...
A little further in the same paragraph you mention the possible
introduction of "read barriers" and "write barriers". Check
Thread.VolatileRead and Thread.VolatileWrite. They don't introduce a
global barrier, but they do so for the variable that needs to be read
or written to (if I understood the docu correctly).
Mmm... the docs really aren't clear. I *suspect* they actually execute
a "real" volatile read/write as per the memory model, but it's hard to
say for sure. I'll mention them in an "unsure" kind of way...
There is not a single word about Thread.Interrupt, what it does and
how it should be used. The same is true for Thread.Abort. I think
these are important and deserve some explanation.


Possibly. I personally think they should be avoided at almost all
costs, but I suppose mentioning that wouldn't be a bad idea :)

--
Jon Skeet - <sk***@pobox.com>
http://www.pobox.com/~skeet
If replying to the group, please do not mail me too
Jul 21 '05 #49

P: n/a
TT (Tom Tempelaere) <"=?Utf-8?B?VFQgKFRvbSBUZW1wZWxhZXJlKQ==?=" <_|\|_0
$P@|/\|titi____AThotmail.com|/\|@P$0_|\|_>> wrote:

[Deriving from thread or not]
What is true for Java isn't necessarily true for other languages.
True - but this is more of a general design issue rather than something
particularly language-specific.
But apart from that, a while ago when I needed threading in MFC/C++ I
wrote a base class to take out all the thread-plumbing code, and
introduce heavy automisation. This feasible by making your own thread
class which expects to be derived from. True, maybe it is not the
best model, but it saves you a lot of code, and it is definitely a
lot more elegant. Honestly, I went one step further and all my
threads can do is execute tasks (also a base class which should
bederived from).
I think you could do all of that without deriving from the base class
though - you could pass the instance of your class a ThreadStart
delegate, and still get it to do all the work.
The point in making the abstraction is to automize and not having to
write code again and again. Plus the elegancy of code, and the ease
of reviewing.
Sure - but I haven't suggested duplicating code. I've just suggested
avoiding inheritence where it's unnecessary, as I believe it is in this
case. If you're not really changing the actual threading behaviour,
just giving it something different to run, using a ThreadStart delegate
to describe what should be run within a class which encapsulates the
rest of the threading behaviour you want.

To be honest, I haven't really *needed* to change the threading
behaviour beyond what a normal Thread.Start does - changing the name,
maybe, but that's about it. What does your class do?
Enfin, this is just my opinion but if I were to write threading code
in Java or any other language, it would probably be the first thing I
do: cover the messy threading details and use your pattern in _every_
language you can think of (if the languages/frameworks are
comparable). My threading code looks _very_ similar in all these
languages (C#, C++ ...) because the interface remains the same.


Doesn't that kinda go against your first sentence though? When some
languages provide facilities which make a design cleaner, why not use
it?

--
Jon Skeet - <sk***@pobox.com>
http://www.pobox.com/~skeet
If replying to the group, please do not mail me too
Jul 21 '05 #50

77 Replies

This discussion thread is closed

Replies have been disabled for this discussion.