ReaderWriterLockSlim + Dispose?

N

NvrBst

One last question today :) Most locks I've used never had a Dispose
(Monitor, ReaderWriterLock, etc). Looking at the
ReaderWriterLockSlim's Dispose method its just cleaning up some
Threading.WaitHandle's.

Whenever I see a Dispose method I always want to call it after I'm
done with the object. All examples I've ever found online don't use
dispose with the ReaderWriterLockSlim. Making my classes that use
this lock IDisposable for this reasion alone is a little frustrating,
and makes the implementation a little harder (since I have to
constantly check to see if the locks disposed, mainly when using it
with a thread pool).

Since only WaitHandles are getting closed, the GC will eventually
clean them if I ignore the Disposing on the ReaderWriterLockSlim? Is
there performance increases in using the disposable WaitHandles vs how
other locks without Dispose() do it?

Thanks :) I'm mostly just curious.

NB
 
N

NvrBst

Are you implying  
that the ReaderWriterLockSlim.Dispose() is less important than for other  
classes that implement IDisposable?

Hehe, aye, that was my question. For example, if it was a file, and
others are potentially wanting to use the file, then I'd rank high on
"definally dispose when your done". But I've never really worked with
these "WaitHandles". Are they low priority to close? Does the OS
just create new ones when people request them, so it doesn't matter
even a little bit if the one ReaderWriterLockSlim is using isn't
closed for a few mins after its done with it? I only see a memory
potential problem here, but its not like WaitHandles are images, no
one really cares about a few bytes till GC collects it (unless its in
some kind of loop). Again, I could be wrong, which is why I'm asking,
basically what I'm missing.

Do you mean that the examples use ReaderWriterLockSlim but don't dispose  
it when the code is done with it?  That's a bug.

http://msdn2.microsoft.com/en-us/library/system.threading.readerwriterlockslim.aspx
http://msdn2.microsoft.com/en-us/library/bb355025.aspx
http://www.bluebytesoftware.com/blog/PermaLink,guid,c4ea3d6d-190a-48f8-a677-44a438d8386b.aspx

To name a few. The 1st MSDN example displays the entire class with no
dispose in it. If you google / google code search
"ReaderWriterLockSlim" I don't think you'll be able to find a code
snipplet or example that uses dispose with RWLS, which I found very
strange. This made me believe that there is something I'm missing?
Possible responsing I might of been expecting after posting was stuff
like:

"Ahh, its because all those examples are used in the main object,
where 99.9% of locks are used. Since the application closes when your
done with the object, that uses the RWLS, no one worries about using
the dispose method for RWLS. Its just correctness for the other 0.1%
of the time someone might be using it in a class that gets created
thousands of times, and thus may want to clean it up after its done.".

Or maybe a "Its a new class so people havn't learned how to use it
correctly yet, I'd highly suggest always calling the Dispose, even if
its in your main object; YOU DEFINALLY DON'T WANT THOSE WAITHANDLES
FLOATING AROUND ANYLONGER THAN YOU HAVE TOO".

Basically, for example, every code snipplet/example on say
StreamReader has the Dispose method with it, why doesn't
ReaderWriterLockSlim?

I don't understand why it's a problem.  Why do you have to check to see if  
the lock is disposed?  Why would it be disposed unless you're sure you're  
done with it (i.e. your own Dispose() method has been called)?

There are reasions. For example, if your sychronizing a thread pool
with RWLS's, then you can't do something like "stop all theads, its
time to clean up" that I know of. A real life example could be, say
your making a macroing program. Its designed to run multiple macros
at the same time and are using the thread pool. Even if you know 5
macros are running (maybe in constant loops), it's time to clean up or
shut down, you don't know how long they are going to take. Since the
client made the macro(script) it could be doing some kind of huge loop
and don't want to wait for it. Cleaning up could result in that
thread trying to use the lock, which is annoying in the case of RWLS-
if its the only disposable thing in the application. With the thread
pool, any situation you don't want to wait for the threads to finish
before cleaning up could result in crashes if you don't check if the
lock is disposed before acquiring it.

Yes yes, you probably don't want to use the thread pool in that type
of situation, but not knowing when the threads are going to be done is
somewhat common, even if you did know you don't always want to wait
around for it to finish (expecially in the case of a thread pool where
you can't interrupt/abort it, and who knows how long till it checks
for a stop variable).

Not necessarily.  If the finalizer thread gets around to running, they  
will eventually get cleaned up, yes.  But there's no guarantee that this 
will ever happen, or when it will happen.  You should not design code that  
relies on that (other than implementing your own finalizer, of course).

Which was another question. Does it really matter if these
"WaitHandles" don't get cleaned up for a while? How does it affect
other things if at all? (Other than maybe some memory that lingering
till GC Collects / AppDomain unloads?). I don't see these WaitHandles
being very big though memory wize though.

[...] If you don't need the added functionality that ReaderWriterLockSlim  
provides, then you might as well use one of the simpler, non-disposable  
synchronization techniques.  Alternatively, you could use the older  
ReaderWriterLock class, and accept the additional performance overhead it  
has.

I was thinking maybe the other implementations use WaitHandles and
just don't clean them up? How many ways can there be for locks to
wait on an event :) Hehe, my last posts last paragraph was more so
asking this question. IE Maybe RWSL, without disposing, is very close
to how Monitor works. It might not be but I was also fishing for any
type of information with locks with the statment ;)


:) Thanks for the comments, I'm still a little curious on information
though about the topic.
 
N

NvrBst

I can't say whether you're missing something.  I do know that a quick  
Google search yielded this link:http://blogs.msdn.com/vancem/archive/2006/03/29/564854.aspx

:) I did see that page before when searching "RWLS + dipose", it was
the only page that showed up on the first google results page that had
relavance. Aye, its not the ReaderWriterLockSlim class, but the
comments at the bottom did give me suspecions that cleaning up RWLS's
might not be needed that much.
In that particular example, you either need to decide whether you're going 
to interrupt the threads that need the lock, or you're going to delay  
cleanup of the lock until the threads are all done.  You can't let the  
threads run _and_ clean up the lock right away.

Aye. I understand this design wise, but, in the case of the thread
pool, you can't interrupt it. And waiting around for the threads to
finish isn't always a perferred solution; maybe the locks were there
to syncronize actions (IE in the case of a chat program, the thread
could lock the send/recive method on a line basis, so if you stop
locking the worst that could happen would be something like Msg1:
"hello jim." Msg2: "test", resulting in "hetleslo jim .\nt". If your
cleaning up, your probably not caring what else the threads going to
do as long as it doesn't crash, etc.
This example stores the instance in a static variable, so IDisposable
wouldn't apply.

Ahh. I always through using static on IDisposable objects was a no
no. But yes, I see now that if the IDisposable object is living to
the end of the application (which is the case of my RWLS's usally),
making them static would simplify my code a lot, and might be the
intended way of using them most the time. I'll look into this
approach more at the very least :)

Thanks for your comments, they were helpful. I usally use Monitor's
(+lock statments) anyway, but wanted to try a few applications with
RWLS and think static might be the way to go for some of the problems
I had with them.

NB
 
N

NvrBst

[...]
In that particular example, you either need to decide whether you're  
going  
to interrupt the threads that need the lock, or you're going to delay  
cleanup of the lock until the threads are all done.  You can't let the  
threads run _and_ clean up the lock right away.
Aye.  I understand this design wise, but, in the case of the thread
pool, you can't interrupt it.

Of course you can.  Not that I was suggesting you use Thread.Abort(), but  
even that should work okay (exceptions are eaten by the thread pool, but  
your own code is aborted...the thread pool will just create a new thread  
later if needed).  In reality, if you need a thread (thread pool or  
otherwise) to be interruptible, you just design it to be so.  Use a  
volatile flag, have the code in the thread check the flag and exit when it 
can.  Or any of a number of other signaling mechanisms that are available.
And waiting around for the threads to
finish isn't always a perferred solution;

If not, then you need to design the thread to be interruptible.  That's  
not a problem.
maybe the locks were there
to syncronize actions (IE in the case of a chat program, the thread
could lock the send/recive method on a line basis, so if you stop
locking the worst that could happen would be something like Msg1:
"hello jim."  Msg2: "test", resulting in "hetleslo jim .\nt".

If I were using a messaging application that produced a result like that,  
I would never ever run it again.  At the very least, it suggests that the  
provider of that application was willing to let less-than-perfect behavior 
occur just to make their own life simpler.  And worst, it's a strong clue  
that the provider may have underestimated the need to produce correct code..

Synchronization exists to ensure correct, predictable behavior from the  
code.  If the code isn't synchronized and is allowing output to be  
intermingled when that wasn't part of the user's intent, who knows what  
else may have been overlooked by the provider?
If your
cleaning up, your probably not caring what else the threads going to
do as long as it doesn't crash, etc.

Well, first of all...the "as long as it doesn't crash" is an important  
consideration: how can you prove that your code won't crash if you've  
abandoned synchronization?  Secondly, if you're cleaning up, then either 
it's okay to interrupt the threads or it's not.  If it is, then you design  
those threads so that they can be interrupted and then you interrupt  
them.  If it's not, then you have no choice but to wait for them to  
finish.  What benefit is there in doing some of the cleanup early, and  
then still waiting on the threads?  Especially when that cleanup removes 
objects that the threads need to use?

Pete

I was talking about the Managed ThreadPool, not a custom made one; the
one you usally access with "ThreadPool.QueueUserWorkItem(...)". After
you start a thread on the threadpool then you can't stop it without a
volatile flag. Maybe you could have the work items list themselves
someplace static so you could call abort from another thread, but I'm
not sure if its possible.
What benefit is there in doing some of the cleanup early, and
then still waiting on the threads? Especially when that cleanup removes
objects that the threads need to use?

I can only list situations off the top of my head (which are quick
examples). I'm sure people run into better (more relivant) situations
naturally, but for example say you want to Restart
(Application.Restart()) the application, or do something else (in the
case of some kind of error, or special event), the user may very of
well been the one to signal the "okay get on with it, I don't care
what threads are doing I want to do something else now", then the user
surly doesn't want to wait for any threads, so you clean up what you
can really quick, set the violet flag to tell the threads to finish,
and start the next task (letting the threads finish gracefully in the
background).
If not, then you need to design the thread to be interruptible. That's
not a problem.

Sometimes, for quick applications at least, re-designing your own
ThreadPools, or such, to fit your situtation perfectly isn't
acceptable (time wise). Instead they use the one already made and
live by the limitations of that implementation, or work around them.

NB
 
N

NvrBst

So was I.


You can call Thread.Abort() on one of those threads as easily as on any  
other thread, provided you have a reference to the thread.  You can always  
get a reference to the thread by getting Thread.CurrentThread inside the  
worker thread.


Yes, it's possible.  And it's exactly what you'd have to do, should you  
decide to use Thread.Abort() to interrupt the thread.

Personally, I feel that using Thread.Abort() is a bad idea, and it's not  
what I was suggesting you might do.  But it certainly is possible.



Such as?  Do you have an example of a scenario in which there's a benefit  
of doing some cleanup early, removing objects that the threads need to  
use, and then still waiting on the threads to complete?


But that's not a correct design, if the threads may want to use any of the 
data structures you are cleaning up before they complete.

You could certainly design an application such that the entire context for 
a session is replaced when restarting, where none of the previous context  
is cleaned up until the threads are done.  But then you're basically  
waiting for the threads to finish anyway, which is what I said you need to 
do.

You simply cannot have valid code in which you clean up data structures  
that threads that continue to run may need to use.  Your only choices are  
to wait for the threads to complete before cleaning up the data structures 
(either letting them run to completion or telling them to interrupt  
themselves), or to change the design so that those data structures are not 
needed by the threads.

Any other design is flawed.



Irrelevant.  That's not what I'm suggesting anyone do.


I don't feel that a coder really has a choice with respect to writing  
correct code.  No programmer deserving of that label has any business  
intentionally writing code that they know to be defective.

Pete

Correct code is in the eye of the beholder. Just because your design
has some limitations or work-arounds doesn't make it defective.
But that's not a correct design, if the threads may want to use any of the
data structures you are cleaning up before they complete.

Who says the threads want to use any of the data structures you are
clenaing up. Furthermore, there is a "disposed" at their disposal.
if(!disposed) use structure.

I always read that ThreadPool threads are lightweight; using them like
suggested isn't correct (design wise as you say). If someone is using
it like that then they probably should redesign the solution to not
use the ThreadPool :p

NB
 
N

NvrBst

You did.  You specifically said you are talking about cleaning up  
synchronization data structures that are used by the threads.

I think you miss read some place, I've never used the word data
structures anywhere here, I said sychronizing actions with the chat
program example if you thought that's the same thing.
If they have the option of not using the structure, then they don't need  
the structure.  In which case you should not have the structure at all.

"They don't need the structure anymore" is what should be said, this
does not mean they didn't need it before the event that signled the
clean up.
Not that the answer to that question would have any relevance to the issue 
I'm writing about in this thread.  But your paragraph is amibguous and I'm  
curious what you actually meant.
Any other design is flawed.

I walk talking about this. To be more specific, basically I said that
if I have a solution that cleans up the object immediatly, lets all
the threads gracefully exit on their own, and doesn't affect the user
(visually) any bit, then not only is this a correct design, but its
more elegant in the respect of using the "disposed" variable as a
violet flag (which, intuativaly, gets checked - and can immidatly exit
the thread when it is checked and found violet).

If the design forcably interrupts/aborts the threads then it can't be
better than the situation above (IMO), and the above is just as
correct as waiting for the threads, which could bring bad cases
depending how long it takes to get between your violet flag checks,
expecially when you don't have to be waiting around (performance
wise).

NB
 
N

NvrBst

rather than simply continuing to process without the
benefit of synchronization as you suggested earlier.

I don't remember ever saying that. If you thought that I'm sorry,
but, as a said, if your checking the disposed flag before using a
disposable object, of course your gonna have an else statment to stop/
clean up if your object is disposed...
The ReaderWriterLockSlim class is a data structure.

If thats the structure your talking about then the "If your thread
wants to use the structure" statment you said is invalid... The
thread doesn't want to use that structure anymore if it's disposed,
else you wouldn't be disposing.
Secondly, what
does that have to do with the paragraph I quoted in which you discussed
the use of the thread pool?

... The part about designing the solution to interrupt the thread.
Specifically if your thread pool is designed to have your worker
threads list themselves in a static variable, thus points to the
"using them like suggested isn't correct", and your statment about
having to interrupt the thread pool, there is no other way. I was
pointing out the valid alternative to "design is flawed".
So, either you introduce a new volatile flag to [...]

I never said replace the violate flag, its like batman and robin. If
the violate flag is batman, then the dispose would be robin. You'd
still need a way to tell the threads to stop without disposing your
object.

Your race condition situation is true for anyone trying to use the
disposed flag in a multithreaded situation. You might as well be
saying that the dispose pattern as we know it is only made for STA
applications. IE Since, is most online examples of the dispose
pattern, the disposed flag gets set after you clean up the managed
resources. What if you call Dispose on an object, then your other
thread checks the disposed flag and enters the if statment, then the
disposed method sets the dispose to false. Application will crash.
Will it ever happen? Most likly never, can it happen? Yes. It's how
multithreaded programers live.

I have to say, this conversation is getting a little pathedic...
Definally off topic...
 
N

NvrBst

I thought it'd be fun to make a "concrete" example :) Please tell me
the incorrect sychronization, or crash. Only requirment is that when
"Hello" gets displayed, "Goodbye" 100% has to be displayed. If it
doesn't then my program failed. Feel free to add Sleeps anywhere. It
uses a disposable sychronization object (RWLS).

class Program {
static void Main(string[] args) {
Class1 myTest = new Class1();
myTest.StartWork();
myTest.Dispose();
Thread.Sleep(1000);
}
}

class Class1 {
private ReaderWriterLockSlim myStringLock = new
ReaderWriterLockSlim();

public void StartWork() {
ThreadPool.QueueUserWorkItem(new WaitCallback(test));
ThreadPool.QueueUserWorkItem(new WaitCallback(test));
}

public void test(Object nullState) {
if(!disposed) myStringLock.EnterWriteLock(); else return;
try {
foreach(string A in new string[] { "hello", "goodbye" })
Console.WriteLine(A);
} finally { if(!disposed) myStringLock.ExitWriteLock(); }
}

private bool disposed = false;
public void Dispose() { Dispose(true); }
protected virtual void Dispose(bool disposing) {
if(!disposed) {
disposed = true;
if(disposing) myStringLock.Dispose();
}
}
}
 
N

NvrBst

I only thought you said that because you did. I'm happy if you want to
retract the previous example, but you did post that scenario as an example.

That was an example to illustrate what would happen if you got past
the (disposable) lock before it disposed, but not result in a crash
and be a valid "not care about the results anymore"; user wise. If it
was before the lock when it disposed, then it wouldn't result in any
output because it'd see the object is disposed and just skip to the
end. It's like using it to generlize the topic to chat programs only
- "whats all this talk about thread pools, I thought we were talking
about chat programs"
Again: I never once suggested that you _should_ be aborting thread pool

Amusing, red herring? You gave the example. Said its the only valid
way if you were in that situation to safly clean up (which I
dissagreeded with). You can keep following the straw man argument
above if you like, but don't tell me to not try to refret the "only"
way to safly dispose the thread pool without waiting argument you
gave, and then call it a red herring.
However, Microsoft does also specifically _recommend_ that
if you implement Dispose() that it be valid to call Dispose() multiple
times safely.

I didn't design the dispose pattern. It comes with a "disposed" flag
that they expect you to use. What if 2 objects try to dispose at the
same time, and both objects try setting the "disposed" flag to false
at the same time? Is the design patter flawed? Not really, the
situation doesn't really occur ever, but it probably can since who
knows when the GC will call it. I wouldn't spend too much time trying
to fix this situation even though it can potentially exsist.
"Most likely never happen" is NEVER an appropriate design
philosophy.

I'm not too sure about this statment... It's not that perfect of a
world.
It's no more off-topic than any other programming design thread

:) It's definally off-topic, but I retract my previous statment. It's
not pathedic, I don't know why I said that. I don't see myself as the
kind of person who has a problem admiting I made a mistake, I just
don't agree with your view.

In my opinion, it's possible to (as reliably as other programs), clean
up an object and not be forced to wait for the threads inside it to
finish, or force threads to stop with an abort. Mainly in the case
where you don't care about the result of the threads anymore. Another
example could be that you send 100 threads off to get an answer, you
got the answer when the 3rd thread returned. You can do clean up and
ignore the other threads as long as you have dispose checks (or try
catch statments (or both)), around the objects you clean up.

Now don't get me wrong. A disposable sychronization lock did seem
very strange to me (point of the topic), and obviously makes using
this type of lock in a situation where you want to do fast clean up
makes it harder (if statmtnet, or try catch statments, everytime you
try and aquire a lock). But this isn't a bad design, the benifites
can be good.


IE in the previous example changing all the "if(!disposed)
myStringLock.EnterWriteLock(); else return;" to "try
{ myStringLock.EnterWriteLock(); } catch { return; }" would fix any
possible crashes, and be a concret example with no possible chance of
crashing, yet have everything synched.

:) NB
 
N

NvrBst

hehe, We got a synch problem going on! :) Aye I seen the case where

1. if(!disposed)
2. disposed = true;
2. RWLS.dispose();
1. RWLS.Aquire Lock

How often? 0.0001% maybe. As listed in previous example try this one

class Program {
static void Main(string[] args) {
Class1 myTest = new Class1();
myTest.StartWork();
myTest.Dispose();
Thread.Sleep(1000);
}



}


class Class1 {
private ReaderWriterLockSlim myStringLock = new
ReaderWriterLockSlim();

public void StartWork() {
ThreadPool.QueueUserWorkItem(new WaitCallback(test));
ThreadPool.QueueUserWorkItem(new WaitCallback(test));
ThreadPool.QueueUserWorkItem(new WaitCallback(test));
ThreadPool.QueueUserWorkItem(new WaitCallback(test));
}

public void test(Object nullState) {
try {myStringLock.EnterWriteLock();} catch(Exception)
{ return; }
foreach(string A in new string[] { "hello", "goodbye" })
Console.WriteLine(A);
try {myStringLock.ExitWriteLock(); } catch(Exception)
{ return; }
}


private bool disposed = false;
public void Dispose() { Dispose(true); }
protected virtual void Dispose(bool disposing) {
if(!disposed) {
disposed = true;
if(disposing) myStringLock.Dispose();
}
}



I added 2 more queue work items because I wanted too :p I don't think
this example can crash. Putting both the !disposed would make
~99.999999% of the users never trigger the catch statment, makes one
almost not want to put it there ;)

NB
 
N

NvrBst

As I noted in my other post, wrapping the call in a try/catch doesn't  
address the underlying design issue, nor does it any way validate the  
design choice.  That said, I find it amusing that you don't like the idea  
of aborting a thread (as well you shouldn't), but you're perfectly happy  
to have it throw an exception that you catch and use to terminate the  
thread.  There's very little difference between the two approaches.

Amusing, are there any other requirments? Am I allowed to use if
statments? :) Its a joke :p. You say "it doesn't crash", but thats
not just the case, you should be saying "it doesn't crash, and
maintains the strick sychronization requirments", and all other
requirments/functions of the program for that matter, with no
possibility of ill effects. It does exactly what the client paid for,
and doesn't have any dead weights running around.

I don't know why you wouldn't call it a valid design, it uses a
boolean to check the object before using it, and in the rare case of
object being disposed between the ")", then it does whatever cleanup
the thread might want to do, and exit, in a valid way to maintain
sychronization.

From what your saying here it's like you've never opened a file in
your life as a programmer (which is the same design pattern as I'm
using). You check the file to see if its valid, and then open it.
Can something grab the file between the check and opening? Definally,
does it make it a bad design? Definally not.

You show me a method of opening a file without a try/catch statment
that doesn't crash, and I'll show you how to check for use of a
disposable object that can be disposed anytime.

Rest of your statments are just plaintly close minded. Telling people
there is no way to do something (correctly), is extremly ignorant, "I
hope no one is paying you for your coding efforts then" with that kind
of mindset, a real programmer see's a problem, and accoplish's it. He
doesn't whine about a try/catch statment, he whines about the
performance/robustness/correctness (problem wise) of a solution.

NB

PS: I'm not saying this is the only way to do things. I've made
programes with all three techniques mentioned here and will agree that
signling threads to finish and waiting for them to finish to dispose
is one way to do it. It doesn't make it the best way for all cases in
the world... unlike your mindset, the world is not that small of a
place.
 
N

NvrBst

Exceptions are costly
Why do you bother checking the boolean at all?

Amusing. You answered your own quesiton, I'm just unsure if you
realized it yourself? Exceptions are costly, you want to minimize
them.
This code performs its duty perfectly:

Other than the fact that your not catching any specific excpetion I
can see. If you wanted a global exception handler there are better
ways.
But that doesn't mean that it's been designed correctly.

It does... Putting a try/catch statment around potentially hazardous
code is exactly what it was designed to do. A good programmer can see
a hazard (even 0.001% case), and fix it with the try statment -
acuratly so that program continues normally as intended.
You should just try to open it. That's your verification right there.

If anyone is readining (which probably not anymore), I'd highly
suggest not listening to this part here. Minimize your excpetions
because they are costly. For example. If pete wants to open a file
he's not 100% sure exsists, he'll "try( File.Open(file) }..." while
what you really should do is a "if(File.Exsists(file)) open" and wrap
that in a try statment if needed for a chance it doesn't exsits after
you just checked it. Unless your very sure the file exsists, you
should do the Exsists check to maxmize performance.

End result, minimize your exceptions people. It's simple math, heres
the equation:

Performance Overhead (ms) = AverageChanceOfExcpetion% * ExceptionCost
+ AverageChanceOfNoneException% * CheckCosts
AKA CheckCosts = ~0.1ms. ExceptionCosts = 1000x that. What you
want to minimize?
He does not just add duct tape until the thing works.

End result, the program, binary wise, would be able to stand up to any
other implementation in any kind of benchmarking, performance,
reliablity tests, not only that, it may even surpass the other
designs. What would the judges(clients) say is better? They don't
care if if one uses a string and the other uses a char[], they care
what gets the job done the fastest.

NB
 
N

NvrBst

[...]
You should just try to open it.  That's your verification right there..
If anyone is readining (which probably not anymore), I'd highly
suggest not listening to this part here.

I would be surprised if there is anyone still following this thread who  
agrees with the design philosophy you're promoting, including your  
suggestion that checking for validity of a file operation before actually  
attempting the operation is a worthwhile thing to do.
[...]
End result, the program, binary wise, would be able to stand up to any
other implementation in any kind of benchmarking, performance,
reliablity tests, not only that, it may even surpass the other
designs.  What would the judges(clients) say is better?  They don't
care if if one uses a string and the other uses a char[], they care
what gets the job done the fastest.

Performance is one the last criterias that should be of concern in program 
design.  Maintainability, simplicity, and reliability are much more  
important.  And for certain, writing bad code just because you think it  
will perform better is just plain wrong.

Pete

You remind me of steve jobs. The crazy crazy personality type who
doesn't care about money, he just wants to make sure everyone does
things his way. In comparason I'd be like Bill Gates, still crazy,
but money crazy.

Incase the analagy is lost on you, Money = Perfomrnace, and Design is
you freaking out about people doing things your way or they are
wrong. It's also like the Nazi's "Theres no place in this world for a
disposable thread sychronization object, GAS THEM".

At least your not saying it wouldn't run worst :p

NB
 
N

NvrBst

I got a relation to Mac and Nazi in one post, your looking evil now,
wuahah.
It is not true that "Money = Performance".

In a metaphore or analogy, it can mean anything I want as long as the
persons smart enough to pass those classic IQ tests (_ is to _ as _ is
to _). Else I have to spoon feed it to them... which isn't as fun.

People think in different ways than others. Forcing someone to write
code in a certain way will only prologe the development process. If a
person wants to dispose straight away and let threads do their thing,
then thats just the way he thinks. For small classes it might even be
the best way IMO. I'd like to see your proof on this method taking
longer/cost more to develop, but I think we both know there isn't any
(expecially if the developer already has that solution in his mind...
you want to force a different thought process into this guys head with
no "good" reasion as I can see). You agree performance wise it isn't
worst, you seem to think it'll be harder to maintain which isn't true
either - as long as its commented right it'll be just as easy as any
other implementation. You seem to think it'll take longer to develop,
for some maybe, the reverse is true for others.

Now if you were giving valid reasion's, even I'd convert and say "oww
definally want to do it that way, I see why now", but the "nope, the
design is flawed because it's not the way I do it. You way works just
as good, but costs more to develop." is a very hard argument stance to
succeed in.

NB
 
N

NvrBst

In the former case, the initial implementation is quick, but then you
create a situation in which you can't tell whether you're catching an
expected exception or one that represents some other bug.

Your catching a very specific exception "Object null exception", other
exceptions wont be caught.
But at the very least you've now committed to a
huge amount of work to wrap every single one of your uses of a disposable
object in a try/catch handler.

It's a lock object... you grab it once and release it shortly after,
probably 75%+ of the time.

Furthermore, if you implement it this way, then users have a lot
easier time using the object. They simply call ".Dispose()" when they
are done with it (little more effort in the design I agree to ensure
proper clean up in all situations). The other solutions put more
effort in different parts of the project (IE clean up methods, thread
stoping. Putting logic like this in the dispose would world very
well), which basically pushes the responsablity onto the user of the
class because you don't want to harder maintance.

In my eyes, maintainence is as simple, it intuativly becomes 'thread
safe', and easier for others to use. Weighed against your "harder to
maintain even with good commenting". It's not a replacment, they both
have different pros and cons. Surly you should be able to see that...

NB
 
N

NvrBst

That's a rather broad assumption,
one that will easily lead to higher maintenance costs if and when it turns
out to be false.

Not really, the try statment is designed to deal with that exact
exception. As a result it'll clean up as intended even if it wasn't
the dispose method that made it null.
Or are you saying that you have this preference to dispose
an object that the threads are using only when it's a synchronization
object,

I was still talking about the example provided. It only has the
sychronization object. Yes all "fast clean" dispose objects need to
be checked before use, but in practice this usally doesn't result in
many objects (~0-1). Can I make a class that uses 100 disposable
objects? Yes. Can I use a bazuka to kill an ant? Yes.
Secondly, there's nothing about your proposed design philosophy that
stated you'd apply it only in very simple scenarios.

It doesn't have to. It depends entirly on the solution. If the
amount of sychronization is simple though this pattern shines most.
Sychronization could be simple even in huge objects as well.
Nothing about the design forces the disposal to be
pushed onto the client of the class.

If your doing automatic clean up (IE in the dispose method), its not
going to work out very well, unless your simply killing the threads.
Anyway you do it, it'll either put more effort into your design
(effort that isn't needed in the other design since this case doesn't
apply / or already implemented), or your pushing it onto the client of
the class to make sure the object can be disposed before they dispose.
Your
proposed design is inherently more complicated and requires more code to
implement.

A couple try/catch statment with some simple clean up in the catch
portion, vs whatever methods your creating (or automatic cleanup
logic). They probably equal out, the only difference is one pushes it
into the code the other localizes it.

NB
 
N

NvrBst

Amusing. We are getting somewhere. Now, for you anyway, its just
"how complex does things have to get before its no good" kinda thing.
I think you'd agree there are real life "work" to be done that fall
into simple 1 page of code. I think, knowing you'd dissagree, that
things can get pretty complex and you'd still do things with the auto
clean up I suggest, and get added benefits that arn't in the other
implementations (see below).
Heres a version of your origional example
without imposing any new
requirements on the client:

Firstly, you should realize your implementation doesn't work like the
inital one. The inital people could use the object a few times.
".StartWork()" ".StartWork()" ".AnotherMethod()", then ".Dispose()".
Yours wouldn't allow this, and as I was saying above, to allow this
type of thing would mean dispose method does the auto cleaning, or you
push the work onto the user. Or you constantly re-create the objects
that you disposed when new work comes [sic].

Secondly, there is more code in your example than the inital, just so
you know.
Either way, that's not "simple".

It is simple if you only have 1 disposable object... even 2 I'd day.
How far can it go up? Note, only disposable objects that are presant
for the enitre class matter. You can still use disposable objects in
any of the methods identically as you would the others - for example
opening a file with a using statment. Your *classes use many
disposable objects among many threads* is rarer than you think.

NB
 
N

NvrBst

I think there is a bug in you code. If you change a = to a +=, it'd
make it more like the origional, however, your code isn't easier to
follow / maintain than the inital... It was actually harder to follow
for me. Maintaince is the same as well. They are both different ways
to accomplish the same thing, I fail to see it better though (inital
is actually easier to implment).

NB
 
N

NvrBst

Since it looks like you have no idea what your arguing for, and since
you failed those classic IQ tests I'll illustrate as plainly as I can
(something I don't like doing... Makes me feel like I'm talking to
kindergardens).

A is to B as C is to D



class A {
private ReaderWriterLockSlim myStringLock = new
ReaderWriterLockSlim();
private int _cthread;
private volatile bool _fDispose;
public void StartWork() {
Interlocked.Add(ref _cthread, 2);
ThreadPool.QueueUserWorkItem(new WaitCallback(test));
}
public void test(Object nullState) {
myStringLock.EnterWriteLock();
foreach (string A in new string[] { "hello", "goodbye" })
Console.WriteLine(A);
myStringLock.ExitWriteLock();
if (Interlocked.Decrement(ref _cthread) == 0 && _fDispose)
Dispose(true);
}
private int disposed = 0;
public void Dispose() {
_fDispose = true;
if (_cthread == 0) Dispose(true);
}
protected virtual void Dispose(bool disposing) { if
(Interlocked.Exchange(ref disposed, 1) == 0) if (disposing)
myStringLock.Dispose(); }
}

class B {
private ReaderWriterLockSlim myStringLock = new
ReaderWriterLockSlim();
public void StartWork() { ThreadPool.QueueUserWorkItem(new
WaitCallback(test)); }
public void test(Object nullState) {
try {
myStringLock.EnterWriteLock();
foreach (string A in new string[] { "hello", "goodbye" })
Console.WriteLine(A);
myStringLock.ExitWriteLock();
} catch ("object is null") { return; }
}
private bool disposed = false;
public void Dispose() { Dispose(true); }
protected virtual void Dispose(bool disposing) {
if (!disposed) {
disposed = true;
if (disposing) myStringLock.Dispose();
}
}
}


class C {
private ReaderWriterLockSlim myStringLock = new
ReaderWriterLockSlim();
private int _cthread;
private volatile bool _fDispose;
public void StartWork() {
Interlocked.Add(ref _cthread, 2);
ThreadPool.QueueUserWorkItem(new WaitCallback(test));
}
public void test(Object nullState) {
myStringLock.EnterWriteLock();
if (Interlocked.Decrement(ref _cthread) == 0 && _fDispose)
{ Dispose(true); return; }
foreach (string A in new string[] { "hello", "goodbye" })
Console.WriteLine(A);
myStringLock.ExitWriteLock();
if (Interlocked.Decrement(ref _cthread) == 0 && _fDispose)
{ Dispose(true); return; }
}
private int disposed = 0;
public void Dispose() {
_fDispose = true;
if (_cthread == 0) Dispose(true);
}
protected virtual void Dispose(bool disposing) { if
(Interlocked.Exchange(ref disposed, 1) == 0) if (disposing)
myStringLock.Dispose(); }
}

class D {
private ReaderWriterLockSlim myStringLock = new
ReaderWriterLockSlim();
public void StartWork() { ThreadPool.QueueUserWorkItem(new
WaitCallback(test)); }
public void test(Object nullState) {
try { myStringLock.EnterWriteLock(); } catch ("object is
null") { return; }
foreach (string A in new string[] { "hello", "goodbye" })
Console.WriteLine(A);
try { myStringLock.ExitWriteLock(); } catch ("object is null")
{ return; }
}
private bool disposed = false;
public void Dispose() { Dispose(true); }
protected virtual void Dispose(bool disposing) {
if (!disposed) {
disposed = true;
if (disposing) myStringLock.Dispose();
}
}
}




Assuming you fix the potential crash at the begining of your program
(hint, not the +=), the only difference is more (ilitalization +
variables + boolean checks) vs try catch. Limitation is you _have_ to
handle the "object is null" in one type (which you can easily bypass
if multiple variables as you do).

As you can see they are identical. It's just for people who are
freaked out about try/catch statments they can put a bunch more
initalization and variables (=maintance) to bypass them.

Ready for the airplain, because here comes more spoonfeeding. See how
you still have to do the boolean check after each use of the object to
make them act the same? Or vice versa not after each?

I didn't use the dispose but just pretend the if statments in the
try's.

Anyway, I don't want to spoonfeed the rest of your comments, I hope
the rest of the comments became clean enough? Basically I'll end this
post with "can't teach old dog new tricks". Have one way in your head
and then it takes a sludge hammer to pound something else in... even
then I don't think there was any success.

NB
 
N

NvrBst

Ahh...the wail of the defeated: switch to ad hominem.

If there were a problem with my code, you'd have simply posted some code  
that demonstrated it (which is what I did to show you the error in your  
own code), rather than waving your hands around, posting insulting  
comments instead of sticking to things that are factually demonstrable.

That's the nice thing about programming.  It's easy enough to just writea  
program.  The person who writes the program has the winning hand over the  
person who just engages in hand-waving.  Where's your program?

Pete

Would of I? Unlike you I don't care about small nit picky situations
which are easy enough to fix. If you can't figure out then get ready
for the air plain because here comes another spoon.

-----Part1
Class1 x = new Class1();

-----Thread 1 and Thread 2 both using x
Thread1: x.SomeWork();
Thread2: x.Dispose();
Thread2: _fIdpsoable = true; Thread Cound = 0, Lock.dispose()
Thread1: *sets thread count to 2*
Thread1: tries to use disposable object == CRASH


Are you gonna use the "Should of said it was thread safe to begin
with, then I would of made it thread safe"? I know you rather compare
a non-thread safe version to a thread safe, and a "exit at end" vs a
"exit anytime" and apples to oranges, it can only help your argument.

Its disgusting that you think using a try/catch is harder to maintain
than updating the state of the system throughout the code.
Its disgusting that you think every human would nit pick at dumb
little stuff like a if statment at the begining to make things thread
safe, like yourself.
Its disgusting that you want to compare apples to oranges, and think
it actually helps your argument.
Its disgusting you can't see that the two implementations are
identical.
its disgusting that you accept one as valid and the other as design
flawed.
Its disgusting that you think you have any right to whine about ad
hominems.
Its disgusting that you wont accept anything that doesn't have a
"name".
Its disgusting that you probably think the above list is all I could
think up of... You should of scean some of the stuff I deleted from
it.

You should be in Pink Flamingos, you'd probably win.

To help you with #2 above, heres a sceanario:
a) "Oww a crash, object null excpetion at ln 1632... Ahh forgot that
ones a fast clean object. Fixed... that was hell to maintain".
b) "A crash..., object null exception at ln 1632... Wait a sec, is
there 5 threads in this or 6? Ahh can't recreate, must be fixed.
Wait a sec this guy over here forgot to up the counter when he started
the thread, thats where the other one came from, not the for loop. Re-
fix, can't recreate. Wait a sec... is the dispose() even being
called... its not... WTH, Its been like that for 3 months now..."

And if you need another spoon, Extra Initalization and extra state to
maintain == easier to maintain? LOL

Oww, and if you want a name how about this "Leave the closet when the
lights turn out, and what happens in the closet stays in the closet,
just don't kill yourself.". Now that it has a name you feel better?

NB
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top