By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
424,851 Members | 1,131 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 424,851 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
Share this Question
Share on Google+
77 Replies


P: n/a
True. If we did not have them, we could implement them with a monitor like
so:
http://www.mvptools.com/Doco/CSharp/Events.htm
Please post or write with errors or comments. Cheers!
--
William Stacey, MVP

"TT (Tom Tempelaere)" <_|\|_0$P@|/\|titi____AThotmail.com|/\|@P$0_|\|_>
wrote in message news:29**********************************@microsof t.com...
"Jon Skeet [C# MVP]" wrote:
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.
[...] 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.)

[...]
--
Jon Skeet - <sk***@pobox.com>


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).
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).
Cheers,
--
Tom Tempelaere.


Jul 21 '05 #51

P: n/a
> 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.

Me too. There is at least two notable exceptions:
1) If your implementing IContainer, then you need to provide SyncRoot. If
you only have one lock, you may as well use "this" and save creating and
storing another object as effect would be the same.
2) If you have a collection (i.e. tree) that takes other objects (i.e.
nodes.) and they all lock on the same lock in Tree. In Node, you don't know
at construction what the lock will be, so you need to set it when added to
the Tree collection so can't use readonly.

Agree with rest. Cheers!

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

P: n/a
William Stacey [MVP] <st***********@mvps.org> 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.

Me too. There is at least two notable exceptions:
1) If your implementing IContainer, then you need to provide SyncRoot. If
you only have one lock, you may as well use "this" and save creating and
storing another object as effect would be the same.


Not unless you document it. People may be locking on your class
elsewhere.
2) If you have a collection (i.e. tree) that takes other objects (i.e.
nodes.) and they all lock on the same lock in Tree. In Node, you don't know
at construction what the lock will be, so you need to set it when added to
the Tree collection so can't use readonly.


Not sure I followed that, but I'm sure you're right :)

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

P: n/a
> Not unless you document it. People may be locking on your class
elsewhere.


..Net does not document this AFAICT, and they do this all the time for
collections. I they are locking on class, then they should lock on SyncRoot
instead as effect would be same. Maybe example of how this could bite you.

Cheers!

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

P: n/a
"Jon Skeet [C# MVP]" <sk***@pobox.com> wrote in message
news:MP************************@msnews.microsoft.c om...
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.


That is just one aspect of threading. For instance you would like to be
notified of thread completion automatically (coded as an event in my
threading class). You would like an easy way to pass parameters to the
thread, just construct the class with the appropriate parameters. You would
like other console output for exceptions in threads (eg unhandled exception
handler, status output, debugging etcetc), write a launching handler with a
surrounding try-catch and output the way you want (this handler is also
necessary for firing the thread completion event), or handle any way you
want automatically. You would like a clean way to seperate thread execution
from thread cleanup for instance (automated of course), and have a clear
indication of how your thread should shutdown gracefully. This last one is
an overridable cancel method. The implemeter of the thread knows how your
thread can shut down gracefully so supplies implementation to do so. You
would like your thread behaviour to be encapsulated in a clean way. The
thread internals are protected (override) and private, and automated. The
public interface consists of threading functionality (start, abort, cancel,
joining, etc) and the things you want to be public (events ...).

Actually in my current threading lib I've gone one step further and added
the abstraction of seperating the thread and that what is executing on it.
That results in a task base class, and a thread class that can execute
tasks. I implemented task completion using WaitHandle (ManualResetEvent, set
when completed) so other threads/tasks can wait for them to finish. In my
current project I need to wait for several tasks to finish in one main
kernel thread, so I collect the task completion handles and wait for them
using WaitHandle.WaitAll. Easy writing.
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


I completely agree. But in this case I think it is a good way to do so. I
have no objection against the fact that .NET made the Thread class sealed.
But my way of coding threads is elegant and automated. What do I need more?
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.
It all depends on what level you are programming I guess.
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?
Not a _class_ but a _progam_. Basically a kernel for an industrial printer
which has to be multi-threaded because things need to execute in parallel
and be synchronized. No UI inside (of course), although a UI component can
plug in to monitor :).
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?


I am using them, of course. Where did I say I didn't?

Actually I like my pattern but I don't see it reflected in any language. It
comes natural to me and obviously unnatural to others. Perhaps it is just a
matter of taste.
--
Jon Skeet - <sk***@pobox.com>


Cheers,
---
Tom Tempelaere
Jul 21 '05 #55

P: n/a
William Stacey [MVP] <st***********@mvps.org> wrote:
Not unless you document it. People may be locking on your class
elsewhere.
.Net does not document this AFAICT, and they do this all the time for
collections.


That doesn't mean it's good practice, necessarily.
I they are locking on class, then they should lock on SyncRoot
instead as effect would be same.
Yes, they should lock on SyncRoot. However, I believe it's a bad idea
to present the opportunity for deadlock due to this for no particular
reason.
Maybe example of how this could bite you.


Normal deadlocking - one thread does:

lock (someCollection)
{
lock (somethingElse)
{
...
}
}

Another thread does:

lock (somethingElse)
{
lock (someCollection.SyncRoot)
{
...
}
}

Deadlock occurs if someCollection==someCollection.SyncRoot.

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

P: n/a
"Jon Skeet [C# MVP]" <sk***@pobox.com> wrote in message
news:MP************************@msnews.microsoft.c om...
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.


Sorry, but if you _own_ the queue then you _know_ where locks would be taken
on this from within the queue. In your case that is not an issue because
there is not a single method that causes a lock on this.
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...


I don't really agree with that. It is one of the known and desirable effects
of Monitor.Enter and Exit.
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 :)


? Interrupt is a necessity if you wait or join ... I don't know what kind of
threads you write,but there are alot of waits in my program and I don't see
how I would stop them from waiting without an interrupt actually. Perhaps
you have an alternative? And what if the thread doesn't repond. It happens
you know. Then abort is about the best you can do. There is no avoiding
there the way I see it. Everything has its purpose in System.Threading. They
didn't put it in for the show.

And indeed it wouldn't be ;)
--
Jon Skeet - <sk***@pobox.com>


Cheers,
---
Tom Tempelaere.
Jul 21 '05 #57

P: n/a
TT (Tom Tempelaere) <"TT \(Tom Tempelaere\)" <_|\|_0P@|/
\|titi____AThotmail.com|/\|@P0_|\|_>> wrote:
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.
That is just one aspect of threading. For instance you would like to be
notified of thread completion automatically (coded as an event in my
threading class). You would like an easy way to pass parameters to the
thread, just construct the class with the appropriate parameters.


You can do that using delegates too.
You woul like other console output for exceptions in threads (eg unhandled exception
handler, status output, debugging etcetc), write a launching handler with a
surrounding try-catch and output the way you want (this handler is also
necessary for firing the thread completion event), or handle any way you
want automatically.
Again, you can do that with a delegate. There's nothing to say you have
to pass the delegate you're given straight to the Thread constructor.
You would like a clean way to seperate thread execution
from thread cleanup for instance (automated of course), and have a clear
indication of how your thread should shutdown gracefully.
Ditto. Supply one delegate for cleanup and one for execution, then
execute one followed by the other.
This last one is
an overridable cancel method. The implemeter of the thread knows how your
thread can shut down gracefully so supplies implementation to do so. You
would like your thread behaviour to be encapsulated in a clean way. The
thread internals are protected (override) and private, and automated. The
public interface consists of threading functionality (start, abort, cancel,
joining, etc) and the things you want to be public (events ...).
Again, that could be abstracted in a way that a delegate could use - it
could take the object to query for when it should be cancelled, etc.
Actually in my current threading lib I've gone one step further and added
the abstraction of seperating the thread and that what is executing on it..
That results in a task base class, and a thread class that can execute
tasks. I implemented task completion using WaitHandle (ManualResetEvent, set
when completed) so other threads/tasks can wait for them to finish. In my
current project I need to wait for several tasks to finish in one main
kernel thread, so I collect the task completion handles and wait for them
using WaitHandle.WaitAll. Easy writing.
Again, I see nothing to stop you from specifying the actual method to
execute using a ThreadStart delegate rather than using inheritance
though.
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


I completely agree. But in this case I think it is a good way to do so. I
have no objection against the fact that .NET made the Thread class sealed..
But my way of coding threads is elegant and automated. What do I need more?


Well, it prevents you from simply running a method in an existing class
which already derives from something else in a different thread (with
your added functionality), for a start. Using a delegate, you'd get all
the benefits of your base class etc but without needing to adjust the
natural class hierarchy just for the purpose of threading.

In my view, a delegate specifies exactly "something to run" - the whole
concept fits threading perfectly. Why not use it, rather than
inheritence?
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.


It all depends on what level you are programming I guess.


Not sure what you mean by that.
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?


Not a _class_ but a _progam_. Basically a kernel for an industrial printer
which has to be multi-threaded because things need to execute in parallel
and be synchronized. No UI inside (of course), although a UI component can
plug in to monitor :).


I was asking about your base class, but I think you've explained a lot
of it above anyway.
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?


I am using them, of course. Where did I say I didn't?


Well, you said the interface remains the same. That means that if
you're using Java, you're not using C#'s events or delegates, for
instance. If you restrict your interface to the intersection of what
all the languages you're interested in support, you're bound to end up
with a less idiomatic interface.
Actually I like my pattern but I don't see it reflected in any language. It
comes natural to me and obviously unnatural to others. Perhaps it is justa
matter of taste.


Probably. I'm afraid I still don't like 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 #58

P: n/a
TT (Tom Tempelaere) <"TT \(Tom Tempelaere\)" <_|\|_0P@|/
\|titi____AThotmail.com|/\|@P0_|\|_>> wrote:
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.


Sorry, but if you _own_ the queue then you _know_ where locks would be taken
on this from within the queue. In your case that is not an issue because
there is not a single method that causes a lock on this.


I realise it's fine in this case, because we lock every time we're
going to do something with the queue anyway - but I prefer the simple
policy which doesn't need me to decide on a case-by-case basis whether
to lock on the object or on something which exists solely for locking
purposes. It's a matter of taste though.
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...


I don't really agree with that. It is one of the known and desirable effects
of Monitor.Enter and Exit.


But it only makes sense if you understand memory barriers to start
with.
Possibly. I personally think they should be avoided at almost all
costs, but I suppose mentioning that wouldn't be a bad idea :)


? Interrupt is a necessity if you wait or join ... I don't know what kindof
threads you write,but there are alot of waits in my program and I don't see
how I would stop them from waiting without an interrupt actually.


With a pulse, for a call to Wait. That's exactly what pulse is for. I
rarely use Thread.Join, but if I was considering the possibility of the
thread hanging, I'd probably use one of the versions that took a
timeout.
Perhaps you have an alternative? And what if the thread doesn't repond. It happens
you know. Then abort is about the best you can do.
Occasionally. A lot of the time I'd rather have a hung thread and abort
the whole process than abort a single thread and have it doing
potentially dangerous stuff.
There is no avoiding
there the way I see it. Everything has its purpose in System.Threading. They
didn't put it in for the show.


Well, I don't have to necessarily agree that everything should be used.
(Look at the Java API, where Thread.stop() is deprecated as being
basically unsafe.)

Besides, I've seen some evidence that an aborted thread in .NET can
have nasty effects, such as coming out of a Wait without regaining a
lock first. I don't want such a thread running around potentially
causing havoc with the rest of my well-threaded code.

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

P: n/a
"Jon Skeet [C# MVP]" <sk***@pobox.com> wrote in message
news:MP************************@msnews.microsoft.c om...
TT (Tom Tempelaere) <"TT \(Tom Tempelaere\)" <_|\|_0P@|/
\|titi____AThotmail.com|/\|@P0_|\|_>> wrote:
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.
That is just one aspect of threading. For instance you would like to be
notified of thread completion automatically (coded as an event in my
threading class). You would like an easy way to pass parameters to the
thread, just construct the class with the appropriate parameters.


You can do that using delegates too.
You woul like other console output for exceptions in threads (eg unhandled exception handler, status output, debugging etcetc), write a launching handler with a surrounding try-catch and output the way you want (this handler is also
necessary for firing the thread completion event), or handle any way you
want automatically.
Again, you can do that with a delegate. There's nothing to say you have
to pass the delegate you're given straight to the Thread constructor.
You would like a clean way to seperate thread execution
from thread cleanup for instance (automated of course), and have a clear
indication of how your thread should shutdown gracefully.
Ditto. Supply one delegate for cleanup and one for execution, then
execute one followed by the other.
This last one is
an overridable cancel method. The implemeter of the thread knows how your
thread can shut down gracefully so supplies implementation to do so. You
would like your thread behaviour to be encapsulated in a clean way. The
thread internals are protected (override) and private, and automated. The
public interface consists of threading functionality (start, abort, cancel, joining, etc) and the things you want to be public (events ...).
Again, that could be abstracted in a way that a delegate could use - it
could take the object to query for when it should be cancelled, etc.
Actually in my current threading lib I've gone one step further and added
the abstraction of seperating the thread and that what is executing on it.
That results in a task base class, and a thread class that can execute
tasks. I implemented task completion using WaitHandle (ManualResetEvent, set when completed) so other threads/tasks can wait for them to finish. In my
current project I need to wait for several tasks to finish in one main
kernel thread, so I collect the task completion handles and wait for them
using WaitHandle.WaitAll. Easy writing.
Again, I see nothing to stop you from specifying the actual method to
execute using a ThreadStart delegate rather than using inheritance
though.
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


I completely agree. But in this case I think it is a good way to do so. I
have no objection against the fact that .NET made the Thread class sealed.
But my way of coding threads is elegant and automated. What do I need

more?

Well, it prevents you from simply running a method in an existing class
which already derives from something else in a different thread (with
your added functionality), for a start. Using a delegate, you'd get all
the benefits of your base class etc but without needing to adjust the
natural class hierarchy just for the purpose of threading.

In my view, a delegate specifies exactly "something to run" - the whole
concept fits threading perfectly. Why not use it, rather than
inheritence?
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.


It all depends on what level you are programming I guess.


Not sure what you mean by that.
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?


Not a _class_ but a _progam_. Basically a kernel for an industrial printer
which has to be multi-threaded because things need to execute in parallel
and be synchronized. No UI inside (of course), although a UI component can
plug in to monitor :).


I was asking about your base class, but I think you've explained a lot
of it above anyway.
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?


I am using them, of course. Where did I say I didn't?


Well, you said the interface remains the same. That means that if
you're using Java, you're not using C#'s events or delegates, for
instance. If you restrict your interface to the intersection of what
all the languages you're interested in support, you're bound to end up
with a less idiomatic interface.
Actually I like my pattern but I don't see it reflected in any language. It comes natural to me and obviously unnatural to others. Perhaps it is just a matter of taste.


Probably. I'm afraid I still don't like it.

--
Jon Skeet - <sk***@pobox.com>
Jon, you can take this to an extreme, and just start coding C again.
Anything can be coded without classes and derivation or interface
inheritance (well, not really because .NET doesn't allow, but that's not the
point). Your call. I prefer OO if it suits and fits what I need to do. And
in this case I've proven for myself (at the very least) that this is the way
to go. But seems like this is not true for others, so in the end everyone
does things that suits him best. In all my threading apps I've been very
happy that I introduced these. There is no hard proof, just experience I
guess. I don't even know why Java threads made you fear thread-derivable
classes. I see only benefits in my approach, and no disadvantages up to this
point. I'm using the pattern for years now. So I'll stick to what I do for
several years with success now.

Your objections are rather thin. Anything can be done in another way; and I
think the alternative approach you suggest results in more code, and less
readable code because in each developer does things his way. The overridable
interface will remain the same for all thread-derivable classes, so no
differences in naming in each thread app because you can't just rename
overridables. A clear pattern that is the same in every app. That is what I
like to see in apps because they are easily reviewed. Everybody in the
company should stick to the pattern so that interchangeability of code
increases. I see every other guy coding threads in a different way and in
the company I work for for instance, I would like them to code threading all
in the same way. The same is true for other domains such as db access and
automisation, UI automisation, etc etc. Sure .NET has a nice library that
(nearly) does it all, but programmers styles differ too much, plus you see
programmers re-coding the same things over and over. I really hate this.

The fact that you have to derive from the base thread class is not bad,
because it should be the only thing it derives from. If you would want to
derive from more than one base class, then your design is obviously wrong. I
wouldn't even dare letting it implement an interface, because that is not
the intention when you derive from the thread. The thread encapsulates
everything that is on the thread, what is needed for the thread to start and
stop gracefully, what is run on the thread etc. It is the controller of what
executes on the thread, and that is a private matter (although the
overridables are protected - in C++ these can be private which is even
better for such patterns). And there is nothing else that such a thread
class should do. You will also want to seal the derived class directly
unless your design says otherwise. I seal by default unless my design says
otherwise.

I am not advocating the introduction of such a class in the .NET library.
The library fits my view of threads and how to make them maintainable. Each
threading app will use the same interface (the english word) of the library
and so all thread apps look the same. In my case they even look the same in
C++. Perfect. All thread main entry's have the same name and signature. All
thread canceling methods have the same name, the same signature. Thread
cleanup methods all have the same name. Ok, the event I used in C# is
different than what I did in C++, but the idea is completely the same
(callback) and is very elegant in both libraries. In my point of view there
is more to a thread than simply that was is run on it.

[]
> > 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?


I am using them, of course. Where did I say I didn't? Well, you said the interface remains the same. That means that if
you're using Java, you're not using C#'s events or delegates, for
Indeed, the interface hides the way threads are written in each language.
Sometimes the outer interface may differ because of different ways of doing
things, eg events (C#) vs listeneres (Java).
instance. If you restrict your interface to the intersection of what
all the languages you're interested in support, you're bound to end up
with a less idiomatic interface.


There are differences in the interfaces of course. Every language has things
that differ from others. Events in c#, listeners in Java and callbacks in
C/C++ (fptr). But the underlying idea remains the same. I'm not restricting
anything, I am assuring that the interface is consistent from a language
neutral point of view. Threading is similar in all languages from a high
level point of view, it's the internals that differ.

Anyway, I gotta go party at the city parade ;-) Ghent
(http://www.cityparade.be)

Cheers,
---
Tom Tempelaere
Jul 21 '05 #60

P: n/a
TT (Tom Tempelaere) <"TT \(Tom Tempelaere\)" <_|\|_0P@|/
\|titi____AThotmail.com|/\|@P0_|\|_>> wrote:

<snip>

I think we'll just have to agree to differ on this one...

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

P: n/a
"Jon Skeet [C# MVP]" <sk***@pobox.com> wrote in message
news:MP************************@msnews.microsoft.c om...
TT (Tom Tempelaere) <"TT \(Tom Tempelaere\)" <_|\|_0P@|/
\|titi____AThotmail.com|/\|@P0_|\|_>> wrote:

<snip>
I think we'll just have to agree to differ on this one...
--
Jon Skeet - <sk***@pobox.com>

It seems. The point we probably agree on then, is that one should find a
good pattern and stick to it in every software module.

Cheers,
---
Tom Tempelaere
Jul 21 '05 #62

P: n/a
"Jon Skeet [C# MVP]" <sk***@pobox.com> wrote in message
news:MP************************@msnews.microsoft.c om...
TT (Tom Tempelaere) <"TT \(Tom Tempelaere\)" <_|\|_0P@|/
\|titi____AThotmail.com|/\|@P0_|\|_>> wrote:
[...]
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...


I don't really agree with that. It is one of the known and desirable

effects of Monitor.Enter and Exit.
[Jon]
But it only makes sense if you understand memory barriers to start
with.

[TT]
True. Maybe memory barriers need
Possibly. I personally think they should be avoided at almost all
costs, but I suppose mentioning that wouldn't be a bad idea :)


? Interrupt is a necessity if you wait or join ... I don't know what kind

of threads you write,but there are alot of waits in my program and I don't see how I would stop them from waiting without an interrupt actually.
[J]
With a pulse, for a call to Wait. That's exactly what pulse is for. I
rarely use Thread.Join, but if I was considering the possibility of the
thread hanging, I'd probably use one of the versions that took a
timeout.

[TT]
One small example of some part in my code (pseudo).

MyTask_1 : Task
MyTask_2 : Task

MyTask_1 t1 = new MyTask_1( ... );
MyTask_2 t2 = new MyTask_2( ... );
TaskExecuter te1 = new TaskExecuter(); // task-thread
TaskExecuter te2 = new TaskExecuter(); // task-thread
te1.AssignTask( t1 );
te2.AssignTask( t2 );
WaitHandle[] taskHandles = {t1.FinishedHandle, t2.FinishedHandle};
WaitHandle.WaitAll( taskHandles );

There is no joining here, just waiting for tasks to finish (the threads
remain alive for performance reasons). The tasks are executed on the
executers which are threads designed to do so. There is no reasonable
timeout because the execution time for the tasks can vary a lot.

I don't see how a Wait + Pulse pattern fits in here. Can one pulse a WaitAll
operation?
Perhaps you have an alternative? And what if the thread doesn't repond. It happens you know. Then abort is about the best you can do.
[Jon]
Occasionally. A lot of the time I'd rather have a hung thread and abort
the whole process than abort a single thread and have it doing
potentially dangerous stuff.

[TT]
I agree with what you say. Recovery is the best you can do. Reboot the
computer, or reboot the process if this possible (I've never done that). It
is probably ez for a service to do that, I should check that...
There is no avoiding
there the way I see it. Everything has its purpose in System.Threading. They didn't put it in for the show.


[Jon]
Well, I don't have to necessarily agree that everything should be used.
(Look at the Java API, where Thread.stop() is deprecated as being
basically unsafe.)

Besides, I've seen some evidence that an aborted thread in .NET can
have nasty effects, such as coming out of a Wait without regaining a
lock first. I don't want such a thread running around potentially
causing havoc with the rest of my well-threaded code.
--
Jon Skeet - <sk***@pobox.com>

[TT]
No indeed you don't Jon. But the deprecated stuff in Java is not to be used
of course. And the fact that it was introduced and then deprecated shows
that Java had (serious?) design mistakes from the start. Let's hope that
this not true for .NET.
Now I never done threading in Java (although I've read about it), but you
made me curious as to why Java/Thread::stop was deprecated. Because people
didn't use it correctly, or because it didn't/couldn't do as was documented,
or it did things which are extremely bad for program state/flow.

I agree with you on Thread::Abort: it is not a safe method to use, and there
are nasty side effects (such as jumping out of finally blocks etc).

Cheers,
---
Tom Tempelaere
Jul 21 '05 #63

P: n/a
"TT (Tom Tempelaere)" <_|\|_0P@|/\|titi____AThotmail.com|/\|@P0_|\|_>
wrote in message news:%d**********************@phobos.telenet-ops.be...
"Jon Skeet [C# MVP]" <sk***@pobox.com> wrote in message

But it only makes sense if you understand memory barriers to start
with.
--
Jon Skeet - <sk***@pobox.com> [TT]
True. Maybe memory barriers need
a seperate chapter. It could bundle the different ways to achieve this.
Enfin perhaps this is beyound the scope of your article. The links are
enough then.
Cheers,
---
Tom Tempelaere

Jul 21 '05 #64

P: n/a
TT (Tom Tempelaere) <"TT \(Tom Tempelaere\)" <_|\|_0P@|/
\|titi____AThotmail.com|/\|@P0_|\|_>> wrote:
It seems. The point we probably agree on then, is that one should find a
good pattern and stick to it in every software module.


Yes - but only until a significantly better pattern is found, at which
point it's worth using that instead. I wouldn't want to rigidly stick
to a pattern I'd found to be inelegant.

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

P: n/a
"Jon Skeet [C# MVP]" <sk***@pobox.com> wrote in message
news:MP************************@msnews.microsoft.c om...
TT (Tom Tempelaere) <"TT \(Tom Tempelaere\)" <_|\|_0P@|/
\|titi____AThotmail.com|/\|@P0_|\|_>> wrote:
It seems. The point we probably agree on then, is that one should find a
good pattern and stick to it in every software module.


Yes - but only until a significantly better pattern is found, at which
point it's worth using that instead. I wouldn't want to rigidly stick
to a pattern I'd found to be inelegant.
--
Jon Skeet - <sk***@pobox.com>

Of course.

Regarding threads, it probably also depends on what the "threading" factor
is in your software. If all you do is simple threading stuff then you don't
need to make a automizing framework for it. But if you find yourself coding
things again and again, then an automizing pattern is nice to have.

Cheers,
--
Tom Tempelaere
Jul 21 '05 #66

P: n/a
TT (Tom Tempelaere) <"TT \(Tom Tempelaere\)" <_|\|_0P@|/
\|titi____AThotmail.com|/\|@P0_|\|_>> wrote:
[TT]
Maybe memory barriers need a seperate chapter. It could bundle the
different ways to achieve this. Enfin perhaps this is beyound the
scope of your article. The links are enough then.
Well, I've already got a section dealing with why memory barriers are
needed and what they're for - I just need to put a link to that section
within the Monitors one.
[J]
With a pulse, for a call to Wait. That's exactly what pulse is for. I
rarely use Thread.Join, but if I was considering the possibility of the
thread hanging, I'd probably use one of the versions that took a
timeout.

[TT]
One small example of some part in my code (pseudo).
<snip>

Ah - I thought you were talking about Monitor.Wait, that being the one
I usually use.

I still don't really like using Thread.Interrupt in such a situation -
if it ends up occurring just after the WaitAll happens, the next
operation will be interrupted, so that needs to be thought about
carefully, etc.
Perhaps you have an alternative? And what if the thread doesn't repond.It

happens
you know. Then abort is about the best you can do.


[Jon]
Occasionally. A lot of the time I'd rather have a hung thread and abort
the whole process than abort a single thread and have it doing
potentially dangerous stuff.

[TT]
I agree with what you say. Recovery is the best you can do. Reboot the
computer, or reboot the process if this possible (I've never done that). It
is probably ez for a service to do that, I should check that...


I believe a service can just die, and with recent versions of Windows
you can tell the service control manager to restart on error. If you're
using an old version of Windows, you could always run the service
within a wrapper which starts the real service and monitors the
process.
[Jon]
Well, I don't have to necessarily agree that everything should be used.
(Look at the Java API, where Thread.stop() is deprecated as being
basically unsafe.)

Besides, I've seen some evidence that an aborted thread in .NET can
have nasty effects, such as coming out of a Wait without regaining a
lock first. I don't want such a thread running around potentially
causing havoc with the rest of my well-threaded code.
[Aargh - *please* don't quote my sig unquoted, as it were - my
newsreader automatically chops off anything below a sig separator, so I
had to manually cut and paste the bit below separately. The sig should
almost *always* be snipped on a reply.]
[TT]
No indeed you don't Jon. But the deprecated stuff in Java is not to be
used of course. And the fact that it was introduced and then deprecated
shows that Java had (serious?) design mistakes from the start.
I wouldn't say that - I suspect Java decided to pretty much copy
existing interfaces, and then realised how unsafe they can be. It's a
shame they didn't realise that before, of course, but I think that
shows them copying other people's design mistakes rather than making
their own new ones. (It's not like the Java libraries are the only ones
to have deprecation...)
Let's
hope that this not true for .NET. Now I never done threading in Java
(although I've read about it), but you made me curious as to why
Java/Thread::stop was deprecated. Because people didn't use it
correctly, or because it didn't/couldn't do as was documented, or it
did things which are extremely bad for program state/flow.
Stopping threads unnaturally is inherently bad - that's the problem.
What do you do with their locks, for instance? If you release them, you
may end with bad state. If you don't release them, you end up with
other threads deadlocking and then *they* have to be stopped.

See
http://java.sun.com/j2se/1.4.2/docs/...itiveDeprecati
on.html

aka http://tinyurl.com/2z6zb
I agree with you on Thread::Abort: it is not a safe method to use,
and there are nasty side effects (such as jumping out of finally blocks
etc).


Does the fact that it's not a safe method to use not mean that it .NET
had serious design mistakes from the start?

(At least Thread.interrupt in Java doesn't allow code to continue from
a wait without regaining the lock it lost - unlike .NET currently,
IIRC!)

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

P: n/a
"Jon Skeet [C# MVP]" <sk***@pobox.com> wrote in message
news:MP************************@msnews.microsoft.c om...
TT (Tom Tempelaere) <"TT \(Tom Tempelaere\)" <_|\|_0P@|/
\|titi____AThotmail.com|/\|@P0_|\|_>> wrote:
[J]
With a pulse, for a call to Wait. That's exactly what pulse is for. I
rarely use Thread.Join, but if I was considering the possibility of the
thread hanging, I'd probably use one of the versions that took a
timeout.

[TT]
One small example of some part in my code (pseudo).
<snip>

Ah - I thought you were talking about Monitor.Wait, that being the one
I usually use.

I still don't really like using Thread.Interrupt in such a situation -
if it ends up occurring just after the WaitAll happens, the next
operation will be interrupted, so that needs to be thought about
carefully, etc.

[TT]: My newsreader has trouble indenting your posts :(. Anyway,

You are absolutely right. I only use it to shut down threads that wait
(WaitHandle waits) or that wait a lot. There should of course be no waiting
operations in the cleanup phase of the thread. I should check on that.
Let's
hope that this not true for .NET. Now I never done threading in Java
(although I've read about it), but you made me curious as to why
Java/Thread::stop was deprecated. Because people didn't use it
correctly, or because it didn't/couldn't do as was documented, or it
did things which are extremely bad for program state/flow.
Stopping threads unnaturally is inherently bad - that's the problem.
What do you do with their locks, for instance? If you release them, you
may end with bad state. If you don't release them, you end up with
other threads deadlocking and then *they* have to be stopped.

See
http://java.sun.com/j2se/1.4.2/docs/...itiveDeprecati
on.html

aka http://tinyurl.com/2z6zb
I agree with you on Thread::Abort: it is not a safe method to use,
and there are nasty side effects (such as jumping out of finally blocks
etc).


Does the fact that it's not a safe method to use not mean that it .NET
had serious design mistakes from the start?

(At least Thread.interrupt in Java doesn't allow code to continue from
a wait without regaining the lock it lost - unlike .NET currently,
IIRC!)
--
Jon Skeet - <sk***@pobox.com>
http://www.pobox.com/~skeet
If replying to the group, please do not mail me too

No it doesn't. I see your point.

Cheers,
---
Tom Tempelaere
Jul 21 '05 #68

P: n/a
>Occasionally. A lot of the time I'd rather have a hung thread and abort
the whole process than abort a single thread and have it doing
potentially dangerous stuff.
Agree to an extent. Your program should cover a thread abort around any
wait anyway, so it is safe if your code handles this condition correctly.
Thread abort has some value in that you don't need to wait(x) just to check
a flag (i.e. stop) and cycle around again This is ok, but causes pooling
and complicates your code. Using abort, you can just wait until the abort
comes (if ever.) I don't really like using exceptions in this way either,
but I see why they did it as apposed to returning false on the wait and
having to check another var if it failed because of a timeout or an abort.
Besides, I've seen some evidence that an aborted thread in .NET can
have nasty effects, such as coming out of a Wait without regaining a
lock first. I don't want such a thread running around potentially


That is a pretty big issue if that happens. Do you have any doco or code
samples that can repro this behavior? TIA

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

P: n/a
> Another thread does:

lock (somethingElse)
{
lock (someCollection.SyncRoot)
{
...
}
}


True, but can dead-lock anything if your code it that way.

--
William Stacey, MVP

Jul 21 '05 #70

P: n/a
William Stacey [MVP] <st***********@mvps.org> wrote:
Occasionally. A lot of the time I'd rather have a hung thread and abort
the whole process than abort a single thread and have it doing
potentially dangerous stuff.
Agree to an extent. Your program should cover a thread abort around any
wait anyway, so it is safe if your code handles this condition correctly.
Thread abort has some value in that you don't need to wait(x) just to check
a flag (i.e. stop) and cycle around again This is ok, but causes pooling
and complicates your code.


It's *much* cleaner, IMO, in terms of knowing what's happening.
Using abort, you can just wait until the abort
comes (if ever.) I don't really like using exceptions in this way either,
but I see why they did it as apposed to returning false on the wait and
having to check another var if it failed because of a timeout or an abort.


Well, Monitor.Wait returns a boolean to say whether or not the lock was
reacquired before the timeout anyway. I would be *very* worried at any
use I had to make of Thread.Abort.
Besides, I've seen some evidence that an aborted thread in .NET can
have nasty effects, such as coming out of a Wait without regaining a
lock first. I don't want such a thread running around potentially


That is a pretty big issue if that happens. Do you have any doco or code
samples that can repro this behavior? TIA


Sure (it's interrupted rather than aborted threads, it turns out, but
even so). This is a modified version of something that was posted a
while ago. Note that if the interrupting thread is the "main" thread it
doesn't seem to happen...

using System;
using System.Threading;

class Test
{
static object padlock = new object();

static Thread waiter;

static void Main()
{
waiter = new Thread (new ThreadStart(LockAndWait));
waiter.Start();

// Let it start waiting
Thread.Sleep(500);

Thread interrupter = new Thread
(new ThreadStart(PulseAndInterrupt));
interrupter.Start();
}

static void PulseAndInterrupt()
{
lock (padlock)
{
Console.WriteLine ("About to interrupt");
Monitor.Pulse(padlock);
waiter.Interrupt();

while (true)
{
Console.WriteLine
("Interrupter thread still got the lock...");
Thread.Sleep(1000);
}
}
}

static void LockAndWait()
{
lock (padlock)
{
try
{
Monitor.Wait(padlock);
}
catch (ThreadInterruptedException)
{
Console.WriteLine
("Caught ThreadInterruptedException");
}
while (true)
{
Console.WriteLine
("Waiter thread got the lock?");
Thread.Sleep(1000);
}
}
}
}

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

P: n/a
William Stacey [MVP] <st***********@mvps.org> wrote:
Another thread does:

lock (somethingElse)
{
lock (someCollection.SyncRoot)
{
...
}
}


True, but can dead-lock anything if your code it that way.


Only if someCollection.SyncRoot==someCollection in this case. Why bring
that possibility into things? Using independent locks makes it a lot
cleaner, IMO.

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

P: n/a
TT (Tom Tempelaere) <"TT \(Tom Tempelaere\)" <_|\|_0P@|/
\|titi____AThotmail.com|/\|@P0_|\|_>> wrote:
Regarding threads, it probably also depends on what the "threading" factor
is in your software. If all you do is simple threading stuff then you don't
need to make a automizing framework for it. But if you find yourself coding
things again and again, then an automizing pattern is nice to have.


Oh certainly I'd agree on that front.

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

P: n/a
TT (Tom Tempelaere) <"TT \(Tom Tempelaere\)" <_|\|_0P@|/
\|titi____AThotmail.com|/\|@P0_|\|_>> wrote:
[TT]: My newsreader has trouble indenting your posts :(.
Please at least cut out my signature then - otherwise it's a pain to
reply to the bit that you write after it. (I'd also suggest changing
your newsreader, or getting OE Quotefix :)
Anyway,

You are absolutely right. I only use it to shut down threads that wait
(WaitHandle waits) or that wait a lot. There should of course be no waiting
operations in the cleanup phase of the thread. I should check on that.


But it'll be interrupted next time it blocks for *any* reason, I
believe. It's probably not too bad if you can absolutely guarantee that
the WaitAll isn't going to return, but I would imagine that the kind of
situation in which you use it would prohibit that 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 #74

P: n/a
> Only if someCollection.SyncRoot==someCollection in this case. Why bring
that possibility into things?
Sorry, not following you. This is possible deadlock regardless if locking
on "this" or another object.
T1
lock(somethingElse)
{
lock(obj.SyncRoot) // or "this"
{
}
}

T2
lock(obj.SyncRoot) // or "this"
{
lock(somethingElse)
{
}
}
Using independent locks makes it a lot
cleaner, IMO.


"this" is independent. Moreover, if you add a 1000 objects that contain
syncRoot objects, you add almost 8K of memory for just the seperate lock
objects(assuming 8 bytes, could be more or less. Sure someone will let me
know.) This may not be much for some folks, but is it is not a trivial
amount of memory.

--
William Stacey, MVP

Jul 21 '05 #75

P: n/a
William Stacey [MVP] <st***********@mvps.org> wrote:
Only if someCollection.SyncRoot==someCollection in this case. Why bring
that possibility into things?


Sorry, not following you. This is possible deadlock regardless if locking
on "this" or another object.
T1
lock(somethingElse)
{
lock(obj.SyncRoot) // or "this"
{
}
}

T2
lock(obj.SyncRoot) // or "this"
{
lock(somethingElse)
{
}
}


That's not the code I posted though. I posted:

<quote>
lock (someCollection)
{
lock (somethingElse)
{
...
}
}

Another thread does:

lock (somethingElse)
{
lock (someCollection.SyncRoot)
{
...
}
}
</quote>

If obj and obj.SyncRoot are different references, there's no dead-lock
in the above code.
Using independent locks makes it a lot
cleaner, IMO.


"this" is independent. Moreover, if you add a 1000 objects that contain
syncRoot objects, you add almost 8K of memory for just the seperate lock
objects(assuming 8 bytes, could be more or less. Sure someone will let me
know.) This may not be much for some folks, but is it is not a trivial
amount of memory.


Well, you've already got *at least* that much memory anyway for the
other objects - and chances are those objects are going to be doing
something useful, especially if they're collections. If you've got a
thousand totally empty collections (which last beyond gen0) I suspect
you're in an unusual situation. (Collections tend to have a bit of
buffer space left over most of the time - do you call
ArrayList.TrimToSize on every ArrayList you create?)

I'd rather have code which was easy to maintain at the cost of a few K
of memory here and there than unmaintainable but efficient code.

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

P: n/a
> That's not the code I posted though. I posted:

Thanks, I know. It does not matter what code you post, it is only one
example. Anytime you have two locks, you could dead lock yourself if you
don't do it right everywhere you lock the objects. It does not matter what
those objects are, from that perspective.
Well, you've already got *at least* that much memory anyway for the
other objects - and chances are those objects are going to be doing
something useful, especially if they're collections. If you've got a
Natuarally. However we were not talking about locking on collection object.
IIRC, you were talking about always using another object just for locking
(i.e. see padlock in prior post.) Moreover, in all the .Net collections (I
think) "this" is what SyncRoot refers to. So your back to what you don't
like if your locking on .Net ICollection objects.
I'd rather have code which was easy to maintain at the cost of a few K
of memory here and there than unmaintainable but efficient code.


We all would agree with that in general I think. But your making a leap
that one will equal the other. I don't think that is true in general.
Anyway, at this point we are not really getting anywhere (and I don't feel
that strongly about this), so please have the last word. Cheers.

--
William Stacey, MVP

Jul 21 '05 #77

P: n/a
William Stacey [MVP] <st***********@mvps.org> wrote:
That's not the code I posted though. I posted:
Thanks, I know. It does not matter what code you post, it is only one
example. Anytime you have two locks, you could dead lock yourself if you
don't do it right everywhere you lock the objects. It does not matter what
those objects are, from that perspective.


But the code I posted was absolutely fine if the sync root isn't the
object itself. Yes, the code leaves you open to the possibility of
deadlock if the implementation uses the object itself as its SyncLock,
but why actually make that risk a problem if you (as the collection
implementer) don't have to?
Well, you've already got *at least* that much memory anyway for the
other objects - and chances are those objects are going to be doing
something useful, especially if they're collections. If you've got a


Natuarally. However we were not talking about locking on collection object.


Were we not? I thought we were. What else has a sync root? We'd been
talking about collections for the past few posts...
IIRC, you were talking about always using another object just for locking
(i.e. see padlock in prior post.) Moreover, in all the .Net collections (I
think) "this" is what SyncRoot refers to. So your back to what you don't
like if your locking on .Net ICollection objects.


Absolutely - I've never suggested that you *should* lock on ICollection
objects.
I'd rather have code which was easy to maintain at the cost of a few K
of memory here and there than unmaintainable but efficient code.


We all would agree with that in general I think. But your making a leap
that one will equal the other. I don't think that is true in general.
Anyway, at this point we are not really getting anywhere (and I don't feel
that strongly about this), so please have the last word. Cheers.


I'm suggesting that it's easier to maintain code if you have a simple
policy - always lock on a private lock or a sync root. If you're
*providing* a sync root, make sure that reference isn't available in
any other way. That policy costs a little bit more memory, but I
believe it makes things simpler.

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

77 Replies

This discussion thread is closed

Replies have been disabled for this discussion.