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

Why does Thread class not support IDisposable?

P: n/a
Why does Thread class not support IDisposable? It's creating quite
some problem. Namely, it can exhaust the resource and you have not
control over it.
Dec 7 '07 #1
Share this Question
Share on Google+
34 Replies


P: n/a
On Dec 7, 2:19 pm, Creativ <GongXinr...@gmail.comwrote:
Why does Thread class not support IDisposable?
Why should it? What would Thread.Dispose actually do?
It's creating quite some problem. Namely, it can exhaust the resource and you
have not control over it.
I've done a fair amount of threading and never got into a situation
where I want to dispose of a thread. Now terminating a thread in a
graceful manner is a different matter - but that's up to the
collaboration between the threads. If you need to "hard" reset a
thread, Abort is your friend - but you should only really do that if
you're tearing down the process (or at least the AppDomain) as it
leaves things in an indeterminate state.

Jon
Dec 7 '07 #2

P: n/a
The scenario I can think of is you create a thread to run some task.
After some runs, quite some threads will be created. After doing that
for a long time, you will have problem in creating Thread and run it
since the CloseHandle will be called in the Finalizer.

Dec 7 '07 #3

P: n/a
On Dec 7, 2:37 pm, Creativ <GongXinr...@gmail.comwrote:
The scenario I can think of is you create a thread to run some task.
After some runs, quite some threads will be created. After doing that
for a long time, you will have problem in creating Thread and run it
since the CloseHandle will be called in the Finalizer.
If you've got that many actual threads, you've got bigger problems
anyway. Have you actually run into this as a problem? Have you seen
anyone else running into this as a problem?

Jon
Dec 7 '07 #4

P: n/a
Yes, I did. Some one created a lot of thread to run some calculation.
Finally he get an error from the numeric library, "Reached Thread
Limit".
I adviced him to use ThreadPool.
But idealy, Thread should support IDisposable since it's a limited
resource.
Dec 7 '07 #5

P: n/a
On Dec 7, 3:17 pm, Creativ <GongXinr...@gmail.comwrote:
Yes, I did. Some one created a lot of thread to run some calculation.
Finally he get an error from the numeric library, "Reached Thread
Limit".
But did you isolate the problem to finalization rather than it
physically trying to run too many threads?
I adviced him to use ThreadPool.
But idealy, Thread should support IDisposable since it's a limited
resource.
But it's not something that *can* be manually released. There's the
handle, but that's it - and I suspect that's not what you were seeing
in the above situation.

As I said before, if you're creating that many threads you will run
into issues regardless.

Jon
Dec 7 '07 #6

P: n/a
As I said before, if you're creating that many threads you will run
into issues regardless.
The problem is as following. Since the Finalize() can be really
delayed, the program will hold a lot of thread handle when those
theads are ready. Implementing a Dispose which call CloseHandle() will
solve this problem.
Dec 7 '07 #7

P: n/a
Changing to ThreadPool, you won't get that kind problem.
Dec 7 '07 #8

P: n/a
On Dec 7, 3:32 pm, Creativ <GongXinr...@gmail.comwrote:
As I said before, if you're creating that many threads you will run
into issues regardless.

The problem is as following. Since the Finalize() can be really
delayed, the program will hold a lot of thread handle when those
theads are ready. Implementing a Dispose which call CloseHandle() will
solve this problem.
Only if you know exactly when to call it - which you typically don't.

There are typically many, many, *many* times more handles available
than you sensibly want to create threads - in other words, if you're
creating enough threads to run into this as a problem, then you're
doing something wrong anyway.

It's like exceptions: there's a slight performance hit, but if you're
throwing enough to see a *significant* issue, then you're almost
certainly misusing exceptions in the first place.

Jon
Dec 7 '07 #9

P: n/a
"Creativ" <Go*********@gmail.comwrote:
Yes, I did. Some one created a lot of thread to run some calculation.
Finally he get an error from the numeric library, "Reached Thread
Limit".
I've gotta agree with Jon on this one. It's not a limitation in Windows
Threading, or the implementation of .Net thread constructs. The problem here
is the algorithm being used is deeply flawed.
I adviced him to use ThreadPool.
But idealy, Thread should support IDisposable since it's a limited
resource.
Threads are special. There's no way an external source can Dispose a thread.
It just isn't something that makes logical sense given the design of threads
in Windows.

An external source can set a flag, "Please Exit when you can", and the
thread can choose to honor that flag. Such a construct is common.

--
Chris Mullins
Dec 7 '07 #10

P: n/a
Creativ <Go*********@gmail.comwrites:
Changing to ThreadPool, you won't get that kind problem.
Of course not, because in this case no new threads are created but the
old ones are recycled. From a performance point of view, this is an
additional benefit of using the ThreadPool, as creating new threads can
be a quite expensive operation (DllMain calls, etc.).

Best regards,
Martin
Dec 7 '07 #11

P: n/a
On Fri, 07 Dec 2007 07:17:46 -0800, Creativ <Go*********@gmail.comwrote:
Yes, I did. Some one created a lot of thread to run some calculation.
Finally he get an error from the numeric library, "Reached Thread
Limit".
I adviced him to use ThreadPool.
And did that solve the problem? Can you post a sample of code that
demonstrates this "Reached Thread Limit" error, as well as an example of
how using ThreadPool avoids the problem?

I agree with Jon: this isn't an issue that IDisposable would address.
There's nothing for the Thread instance to dispose. If you are reaching
some limit on the number of threads, it's because the threads you've
already started haven't exited yet. Even assuming that Thread did
implement IDisposable, it would be a serious problem to dispose the object
before you're done with it (that is, before the thread has finished).

The solution to your friend's problem is to not start new threads before
the previously started ones have exited.

Using the ThreadPool might work around the issue, but only because it
won't allow additional new threads to begin execution as long as the
previously started threads haven't finished. Assuming that the original
bug causing the previously started threads to not finish isn't fixed, then
all that using ThreadPool will do is hide the "Reached Thread Limit" error
with a "my processing never completes" bug, or even an out-of-memory error
(eventually, the queue of threads will get so large that the program runs
out of memory...it might take awhile, maybe a very long while depending on
how the threads are started, for this to happen though).

Just because the "Reached Thread Limit" error goes away when you use
ThreadPool, that doesn't mean you've fixed your bug.
But idealy, Thread should support IDisposable since it's a limited
resource.
Being a "limited resource" isn't what defines whether something implements
IDisposable or not.

Memory is a limited resource. A simple Array object consumes this limited
resource. Yet, there's absolutely no reason for Array to implement
IDisposable, and implementing IDisposable on Array would not in any way
get around exhausting the limited resource through use of an Array.

The bottom line here: reaching the maximum number of threads is
representative of a bug in the code using the Thread class, and is _not_
representative of a defect in the design of the Thread class.

If you can post some sample code that demonstrates the problem, we'll be
able to point out the error in the code that's leading to the problem.
The "problem" being described is a non-problem, but we can at least offer
some advice as to how not to write buggy code. :)

Pete
Dec 7 '07 #12

P: n/a
On Fri, 07 Dec 2007 09:01:51 -0800, Martin Carpella
<ma*************@gmx.netwrote:
Creativ <Go*********@gmail.comwrites:
>Changing to ThreadPool, you won't get that kind problem.

Of course not, because in this case no new threads are created but the
old ones are recycled.
Not if the basic logic causes the application to run out of threads
without ThreadPool. In that case, without a change to that basic logic,
using a thread pool the old ThreadPool threads aren't recycled because the
original delegate executing in the thread won't exit.

Instead, you just an ever-growing queue of new work items for the thread
pool.
From a performance point of view, this is an
additional benefit of using the ThreadPool, as creating new threads can
be a quite expensive operation (DllMain calls, etc.).
It's true, using a thread pool can be a nice way to handle certain
aysynchronous operations. But if you've got a bug with regular threads,
you've still got a bug using thread pool threads.

Pete
Dec 7 '07 #13

P: n/a
"Peter Duniho" <Np*********@nnowslpianmk.comwrites:
Instead, you just an ever-growing queue of new work items for the thread
pool.
Yes, I completely agree with you. In my opinion it is exactly this queue
which will "solve"/hide the error, as there are never "too many"
threads, as the ThreadPool won't grow beyond its maximum thread count.
It's true, using a thread pool can be a nice way to handle certain
aysynchronous operations. But if you've got a bug with regular threads,
you've still got a bug using thread pool threads.
Again, agreed. If the problem is that there are too many concurrent
requests, though, the TreadPool _could_ be the solution, as, as you
mentioned, the work items get queued instead of spawning a new thread.

Best regards,
Martin
Dec 7 '07 #14

P: n/a
You're still going to have problems.

If you've got all of the threadpool threads busy, you may end up deadlocking
the entire threadpool.

The think that keeps being danced around, is that a solution like this
performance waaaaay worse than a single-threaded soultuion. You're using all
system resources creating and scheduling threads. Almost no work is being
done towards your actual problem set.

Abuse of threads, be they custom threads, threadpool threads, IOCP threads,
or some new type I'm not aware of, all hurts performance and is an indicator
of a fundamentaly flawed desing.

.... breaking your work down into chunks, and passing those chungs to a set
of threads via a queue, is often the right design. You just can't point
1000+ threads at the queue and say "go!". At least not on a 1x, 2x, 4x, 8x
processor box.

--
Chris

"Martin Carpella" <ma*************@gmx.netwrote in message
news:87************@msgid.carpella.net...
"Peter Duniho" <Np*********@nnowslpianmk.comwrites:
>Instead, you just an ever-growing queue of new work items for the thread
pool.

Yes, I completely agree with you. In my opinion it is exactly this queue
which will "solve"/hide the error, as there are never "too many"
threads, as the ThreadPool won't grow beyond its maximum thread count.
>It's true, using a thread pool can be a nice way to handle certain
aysynchronous operations. But if you've got a bug with regular threads,
you've still got a bug using thread pool threads.

Again, agreed. If the problem is that there are too many concurrent
requests, though, the TreadPool _could_ be the solution, as, as you
mentioned, the work items get queued instead of spawning a new thread.

Best regards,
Martin

Dec 7 '07 #15

P: n/a
On Fri, 07 Dec 2007 15:06:27 -0800, Chris Mullins [MVP - C#]
<cm******@yahoo.comwrote:
You're still going to have problems.
Always?
If you've got all of the threadpool threads busy, you may end up
deadlocking
the entire threadpool.
Or it may exactly the right thing to do. It just depends.
The think that keeps being danced around, is that a solution like this
performance waaaaay worse than a single-threaded soultuion.
A solution like what? In all cases?
You're using all
system resources creating and scheduling threads. Almost no work is being
done towards your actual problem set.
How do you know that for sure? I haven't seen any specifics in this
thread regarding what "work" is being done. How could we possibly know
that "almost no work" is being done? At the very least, up to the same
number of threads as CPU cores, if the work can be done concurrently in a
thread pool then performance should definitely improve. For work that is
primarily i/o, you could easily exceed the number of CPU cores, and even
consume the entire thread pool (*), while still enjoying a performance
improvement.

(*) Of course, completely consuming the thread pool interferes with other
uses of the thread pool. I'm assuming an abstract case where the "thread
pool" in question is dedicated to the problem at hand.
Abuse of threads, be they custom threads, threadpool threads, IOCP
threads,
or some new type I'm not aware of, all hurts performance and is an
indicator
of a fundamentaly flawed desing.
_Abuse_ of threads, yes. But using a thread pool generally? Even if the
entire thread pool winds up busy at some point? That's impossible to say
without knowing more about the work being done.
... breaking your work down into chunks, and passing those chungs to a
set
of threads via a queue, is often the right design. You just can't point
1000+ threads at the queue and say "go!". At least not on a 1x, 2x, 4x,
8x
processor box.
Well, if you have 1000 chunks of work, and you're using a thread pool, I
see no reason at all to not just point 1000 of those chunks at the thread
pool and let it go. The queueing in the thread pool is supposed to manage
the number of actual concurrent threads. For i/o, a number much larger
than the number of CPU cores is likely to be fine, and for CPU-bound stuff
of course you might want a dedicated thread pool that restricts the number
of active threads to the number of CPU cores. But even in that case,
enqueuing all 1000 chunks at once seems fine to me.

For that matter, I guess I don't really know for sure that in the case of
the problem described in this thread that a thread pool isn't an
appropriate solution. The original problem seems to describe a situation
where thread instances are added gradually, eventually reaching the
maximum number. But if a large number are added all at once, and over
time the thread procedures do exit and on average more threads are not
created than can complete, using a thread pool might in fact be a good
solution to the OP's problem.

But even in that case, it's not because Thread should implement
IDisposable. :)

Pete
Dec 8 '07 #16

P: n/a
"Peter Duniho" <Np*********@nnowslpianmk.comwrote:
On Fri, 07 Dec 2007 15:06:27 -0800, Chris Mullins [MVP - C#]
<cm******@yahoo.comwrote:
>You're still going to have problems.

Always?
Probably not always, but certainly 'often'. It's going to depend on what
kind of work you're posting to the threadpool. Given that some kinds of work
will kill it, and other won't, and it hard to predict which is which, that
puts it squarely in the "don't use it" category.

The problem (as we both know) is doing I/O from a threadpool thread, risks
deadlock. It's not even I/O - but anything that requires a callback, which
in turn requries a threadpool, which isn't available.
>The think that keeps being danced around, is that a solution like this
performance waaaaay worse than a single-threaded soultuion.

A solution like what? In all cases?
A solution that creates so many threads the O/S comes back and says, "No
more threads!". This likley implies thousands of threads.
>
>You're using all
system resources creating and scheduling threads. Almost no work is being
done towards your actual problem set.

How do you know that for sure?
Well, if so many threads exist that the O/S is unable to create more
threads, then it's so busy scheduling the existing threads that nothing is
going to get done. I suppose you could muck with priorities to get around
that, but... yea, I feel pretty confidant about that! :)
At the very least, up to the same number of threads as CPU cores, if the
work can be done concurrently in a thread pool then performance should
definitely improve.
Totally, with ya 100%. But with an error described as, "Finally he get an
error from the numeric library, "Reached Thread Limit"." then I'm thinking
thousands and thousands of threads. I could certainly be wrong, but that's
my assumption.
For work that is primarily i/o, you could easily exceed the number of CPU
cores, and even consume the entire thread pool (*), while still enjoying
a performance improvement.
Heh. Been there. Done that.

.... by mistake in a few cases, much to my chagrin.

me: !threads
sos: "Listing all 1050 threads"
me: "wow. I can't be reading that right."

<silence/>

me: "Oh. that would explain it."
_Abuse_ of threads, yes. But using a thread pool generally? Even if the
entire thread pool winds up busy at some point? That's impossible to say
without knowing more about the work being done.
Well, with the growth of the thread pool to 250 threads per core, that means
up to 1000 threads active on a quad-core system.

http://www.bluebytesoftware.com/blog...dbd7d2108.aspx

These numbers really scare me away from the thread pool...
But even in that case, it's not because Thread should implement
IDisposable. :)
Hey, no drifting back to the original topic, that's bad UseNet form!

--
Chris Mullins
Dec 8 '07 #17

P: n/a
On Fri, 07 Dec 2007 17:28:50 -0800, Chris Mullins [MVP - C#]
<cm******@yahoo.comwrote:
[...]
>>The think that keeps being danced around, is that a solution like this
performance waaaaay worse than a single-threaded soultuion.

A solution like what? In all cases?

A solution that creates so many threads the O/S comes back and says, "No
more threads!". This likley implies thousands of threads.
But the point of the thread pool is that you're not actually creating new
threads. I read your post to suggest that even using a thread pool
"you're still going to have problems". To me, the whole point of having a
thread pool is not only to provide an easy way to assign tasks to separate
threads, but also to have the queuing behavior to ensure that you don't
have too many active threads at once.

If the built-in thread pool doesn't provide this (see below), then one can
create their own thread pool. It seems to me that generally speaking, the
use of a thread pool _can_ in fact avoid some problems. Will it actually
fix the problem here? Not sure. But I don't think it's true that even
using a thread pool you're necessarily going to have problems. Depending
on the algorithm being implemented, a thread pool could in fact solve
whatever basic architectural problem exists, without introducing new ones.

It might not, but I don't think you can say that it always won't.
Well, if so many threads exist that the O/S is unable to create more
threads, then it's so busy scheduling the existing threads that nothing
is
going to get done.
That assumes that you're creating a new thread for each task item though.
The point of the thread pool comments is that it can avoid this, by
ensuring that the number of actual threads created is well below the OS
limit, and within a reasonable number of concurrently active threads.

Again, I read your comment to mean that even using the thread pool you'd
have the same problems as if you created a new thread for each task item.
That's what I'm wondering about.

By the way, as long as the threads are CPU-bound, I don't actually believe
that scheduling overhead will prevent work from getting done. Unless a
thread blocks, it's going to use up the roughly 50ms quantum given to it,
and a context switch is a _MUCH_ smaller amount of time than that. The
biggest problem in a scenario like that is the potential for causing
frequent cache misses on a CPU architecture that has a huge dependency on
the cache to perform well. Another problem is that properly managed, you
could avoid the context switch altogether so any context switching
represents some reduction in performance. Yet another problem is that if
all the tasks are run concurrently, then you don't get any results until,
right at the end, you get all the results. The latter problem is mainly
one of user responsiveness; it doesn't relate to overall throughput.

The context switch is a performance issue, granted...but it's not like
having even more threads makes it worse. Once you have twice as many
threads as CPU cores, you guarantee that each thread will have to give up
the CPU at the end of its timeslice. More threads doesn't make that any
worse. And while I don't have the numbers for the cost of a
context-switch off the top of my head, I would be surprised if it took
more than 1% of the timeslice, and surely it's not more than 10%. If
anything, I suspect that the reason a timeslice is so large still is that
otherwise the context switch _would_ be a large percentage of the time.

So, context switching causes overhead? Yes, no question. But is it a
disaster? I've got no reason to believe it has to be. Context switching
alone isn't going to cause it to be, and that's the only real constant.
Trashing the cache is a big potential problem as well, but that can be
avoided, whether by design or simply having computational tasks that don't
need a lot of memory and don't experience cache aliasing. Also caches are
pretty huge these days, which helps a lot.
[...]
Totally, with ya 100%. But with an error described as, "Finally he get an
error from the numeric library, "Reached Thread Limit"." then I'm
thinking
thousands and thousands of threads. I could certainly be wrong, but
that's
my assumption.
Sure, thousands and thousands of threads in the original implementation.
But not with a thread pool.
[...]
Well, with the growth of the thread pool to 250 threads per core, that
means
up to 1000 threads active on a quad-core system.
Really? The original default was, what? 25 threads? They've upped that
to 250 threads? A 10X increase?

This is where I become concerned that the default thread pool doesn't
provide an appropriate level of CPU management.

Note also that in 32-bit Windows, there's by default a practical limit of
about 2000 threads per process (and that doesn't even account for other
memory consumption of the process), even ignoring the context switching
overhead. Given that the IOCP thread pool is by default 1000 threads,
that means that on a 4-core system those two thread pools alone basically
could consume the entire quota for a process, if they were both utilized
to their maximum capacity. When they dramatically increased the number of
threads in the thread pool, did they also reduce the stack size for each
thread?

But even assuming that they've messed up the thread pool in this way, one
could easily just create their own, or even just change the default
maximum to better suit the application's needs and behavior (i.e. put it
back to 25 like it was).

I still don't see how using thread pools _guarantees_ the same problems as
the original "create thousands of threads" implementation.
http://www.bluebytesoftware.com/blog...dbd7d2108.aspx

These numbers really scare me away from the thread pool...
Well, I agree with you that 250/CPU is way too many for the thread pool.
I also think the justification (avoiding thread pool deadlock) is lame.
So they're going to trash the behavior of nominally well-behaved
applications just as a work-around to accomodate badly-behaved
applications? Ick.

Even his example seems lame to me; no competent programmer ought to be
writing code like that, and if they do they _deserve_ to get deadlocked.
So there!

And raising the thread pool number of threads doesn't make the problem go
away; it just delays it. His example only requires sorting an array of
30-some elements to deadlock (according to him...I haven't bothered to
think about why his number is the magic number, but it sounds about right
for a 25-thread pool), so even expanding the thread pool by a factor of
10X doesn't seem likely to me to change the problem enough to avoid having
it happen in real-world situations.

Personally, I think it ought to be an exception for code running in a
thread pool thread to intentionally block at all (that is, just block for
the sake of blocking...I'd grant some leeway on blocking because of i/o,
but only if that i/o didn't involve direct user input). The whole point
of a thread pool is that the things you run on it are reasonably short and
don't sit around waiting, on other threads or on the user. Calls to
Join(), WaitOne(), Sleep(), etc. should just be prohibited.

Crazy? Maybe. But I think if we're going for crazy solutions to bad
programmers, I'd rather just make it painful for them right away, rather
than to just try to sweep their bugs under the rug.

Sorry...I suppose I could be preaching to the choir on this question about
the 250/CPU max issue.
>But even in that case, it's not because Thread should implement
IDisposable. :)

Hey, no drifting back to the original topic, that's bad UseNet form!
Sorry. I forgot the rule of primary Usenet entropy. :)

Pete
Dec 8 '07 #18

P: n/a
Let me elaborate my question more. When the execution is ready, when
CloseHandle will be called?
I cannot find the details about that. If CloseHandle is called in the
Finalizer, call CloseHandle in Dispose would give a user an
deterministic way to close that handle.
Dec 9 '07 #19

P: n/a
Chris Mullins [MVP - C#] wrote:
"Creativ" <Go*********@gmail.comwrote:
>Yes, I did. Some one created a lot of thread to run some calculation.
Finally he get an error from the numeric library, "Reached Thread
Limit".

I've gotta agree with Jon on this one. It's not a limitation in Windows
Threading, or the implementation of .Net thread constructs. The problem here
is the algorithm being used is deeply flawed.
>I adviced him to use ThreadPool.
But idealy, Thread should support IDisposable since it's a limited
resource.

Threads are special. There's no way an external source can Dispose a thread.
It just isn't something that makes logical sense given the design of threads
in Windows.

An external source can set a flag, "Please Exit when you can", and the
thread can choose to honor that flag. Such a construct is common.

--
Chris Mullins

I think there is a misunderstand between what the poster is asking for
and what you all are answering.

The problem, if I'm correct, isn't that he wants to dispose of a running
thread. The problem is that the handles being used by the thread is kept
until the object is collected, and thus the finalizer is called. This
happens, naturally, at a nondeterministic time after the point when the
thread method has stopped running.

To test this hypothesis, I created a list of 1000 thread objects. I
created the threads and kept then in a suspended state waiting for an
event, and watched the number of handles soar. Each thread object uses
up 5 handles, as reported by taskinfo (www.iarsn.com).

Then I asked them all to finish, but kept the references to the thread
objects in my list, this made all the thread methods exit, as I could
see lots of messages in the output window about threads exiting, and
TaskInfo also removed all the threads from its list. However, the
handles were still in use.

Then finally I cleared the list and forced a collection, at this point
the handle count reported by TaskInfo dropped back to the initial level.

Note that I agree with the sentiment of the posts here, if you get to
the point where .NET complains about reaching the limit then you're most
likely doing something wrong, but I also got to ask if the thread object
is regarded so special as to not allow us to clean up the resources
deterministically. And note once again that I'm not talking about
yanking out the handles underneath the running thread.

Since I can't seem to find any method on the Thread object that would
mimick the usual case of a Close or Dispose method, IMO the handles will
be "in use" until the object is disposed of. In all other discussions I
see about using scarce resources in .NET, dispose is the way to go, but
Thread is apparently special. How so? What am I missing here?

Or is there some special case code in there that monitors the amount of
lingering to-be-collected Thread objects and forces a collection if the
number gets too high? Is it just me or could this lead to a strange
problem in a system that uses a lot of threads but for some reason has
so much memory that GC is happening at very long intervals?

--
Lasse Vågsæther Karlsen
mailto:la***@vkarlsen.no
http://presentationmode.blogspot.com/
Dec 9 '07 #20

P: n/a
Yes. That's what I'm wondering about.
Since there is no direct relation between memory consumption pressure
and "thread consumption pressure", we cannot rely solely on GC. That's
why I'm wondering why Thread doesn't implement Dispose.
Dec 9 '07 #21

P: n/a
Lasse Vågsæther Karlsen <la***@vkarlsen.nowrote:
I think there is a misunderstand between what the poster is asking for
and what you all are answering.

The problem, if I'm correct, isn't that he wants to dispose of a running
thread. The problem is that the handles being used by the thread is kept
until the object is collected, and thus the finalizer is called. This
happens, naturally, at a nondeterministic time after the point when the
thread method has stopped running.
That's what I'd understood as well. Hence:

<quote>
There are typically many, many, *many* times more handles available
than you sensibly want to create threads - in other words, if you're
creating enough threads to run into this as a problem, then you're
doing something wrong anyway.
</quote>

Basically, I can see why it's a *theoretical* problem - but it only
becomes a *practical* problem if you've got bigger problems anyway.

<snip>
Since I can't seem to find any method on the Thread object that would
mimick the usual case of a Close or Dispose method, IMO the handles will
be "in use" until the object is disposed of. In all other discussions I
see about using scarce resources in .NET, dispose is the way to go, but
Thread is apparently special. How so? What am I missing here?
I *suspect* that the problem is that closing the handle of a thread
prematurely would have nastier effects than closing the handle of, say,
an Image before something else tries to access it.

--
Jon Skeet - <sk***@pobox.com>
http://www.pobox.com/~skeet Blog: http://www.msmvps.com/jon.skeet
World class .NET training in the UK: http://iterativetraining.co.uk
Dec 9 '07 #22

P: n/a
On Sun, 09 Dec 2007 01:41:18 -0800, Creativ <Go*********@gmail.comwrote:
Let me elaborate my question more. When the execution is ready, when
CloseHandle will be called?
"Ready"? Do you mean, when the start delegate for the thread returns?

I'm not sure that in .NET you can say that it does. In at least some
incarnations of the framework, there is not a 1-to-1 correspondence
between a .NET managed thread, and an actual OS thread.
I cannot find the details about that. If CloseHandle is called in the
Finalizer, call CloseHandle in Dispose would give a user an
deterministic way to close that handle.
If you really want to know, you could use Reflector to look at the
implementation of Thread and see what it does. But I would be surprised
if the Thread class even has a finalizer. Generally speaking, a finalizer
and IDisposable go hand in hand. There may be exceptions, but surely they
aren't frequent.

Given that, if closing the thread handle is a requirement to free up the
address space used by the thread (I'm not sure it is...never really gave
it much thought, I admit since normally the right way to do it is not run
so many threads it becomes an issue), I would expect that the Thread class
would close the handle immediately after the start delegate has returned.

The bottom line: you don't need IDisposable on the Thread class. If
you're running into resource management problems with the Thread class,
it's not because it doesn't implement IDisposable.

Pete
Dec 10 '07 #23

P: n/a
"Chris Mullins [MVP - C#]" <cm******@yahoo.comwrites:
If you've got all of the threadpool threads busy, you may end up deadlocking
the entire threadpool.
You can exhaust the threadpool, of course, but that's when items start
to get queued.
>The think that keeps being danced around, is that a solution like this
performance waaaaay worse than a single-threaded soultuion. You're using all
system resources creating and scheduling threads. Almost no work is being
done towards your actual problem set.
Well, isn't that the reason why the threadpool should have a sensible
maximum number of threads? To keep the scheduling overhead sensible and
avoid too much concurrency. That's why the built-in threadpools size
depends on the number of CPUs/cores, isn't it?
... breaking your work down into chunks, and passing those chungs to a set
of threads via a queue, is often the right design. You just can't point
1000+ threads at the queue and say "go!". At least not on a 1x, 2x, 4x, 8x
processor box.
Well, you'd have around 25-50 threads on a typical thread pool on a
single/dual-core computer. If you have more tasks, they'll get queued
up.

Best regards,
Martin
Dec 10 '07 #24

P: n/a
On Mon, 10 Dec 2007 00:45:10 -0800, Martin Carpella
<ma*************@gmx.netwrote:
"Chris Mullins [MVP - C#]" <cm******@yahoo.comwrites:
>If you've got all of the threadpool threads busy, you may end up
deadlocking the entire threadpool.

You can exhaust the threadpool, of course, but that's when items start
to get queued.
That's not what Chris is talking about. He's referring to a design bug in
which code executing in a thread pool thread waits for another thread
executing in a thread pool thread to complete. IMHO, no one should ever
write code like this anyway, but if they do they can create a situation
where all of the thread pool threads are stuck waiting on other threads
(typically threads they created, but that's not important) that have all
been queued and won't execute until a currently running thread pool thread
finishes its task.
>>The think that keeps being danced around, is that a solution like this
performance waaaaay worse than a single-threaded soultuion. You're
using all
system resources creating and scheduling threads. Almost no work is
being
done towards your actual problem set.

Well, isn't that the reason why the threadpool should have a sensible
maximum number of threads? To keep the scheduling overhead sensible and
avoid too much concurrency. That's why the built-in threadpools size
depends on the number of CPUs/cores, isn't it?
As I wrote in my other post, once you have more than twice as many
constantly executing threads than CPUs, there's no additional overhead
from scheduling. The only real overhead there is the context switch, and
once all threads are taking their entire timeslice, you don't get any more
context switch overhead with more threads than you would have with that
baseline number.

With more threads comes the greater possibility of repeatedly voiding the
CPU cache of useful data, but again even a small number of threads can do
that. I don't think that the possibility is a significant factor in the
choice of the number of threads in a thread pool.

Instead, I think that the number of threads in a thread pool is primarily
dictated by two balancing factors: the cost of a thread, and the need to
ensure that you always have some thread runnable, to maximize your use of
the CPU. When your tasks are entirely CPU-bound, this is less of an
issue: just make one thread for each CPU and let it run. But many
problems are at least partially i/o-bound. In that case, you want enough
threads to ensure that when some of those threads wind up waiting on i/o,
there are other threads that can be executed taking advantage of the
available CPU.

On the other side of the balance is the cost of a thread. With 1MB of
stack space for a thread (by default), and a 2GB limit in a processes
virtual address space, you can have only 2048 threads total, in theory (of
course, in practice it's even less than that because other things compete
for the process address space). Without this cost, you would just let the
thread pool grow unbounded, because after all that would maximize your
chances of always having something to run.
>... breaking your work down into chunks, and passing those chungs to a
set
of threads via a queue, is often the right design. You just can't point
1000+ threads at the queue and say "go!". At least not on a 1x, 2x,
4x, 8x
processor box.

Well, you'd have around 25-50 threads on a typical thread pool on a
single/dual-core computer. If you have more tasks, they'll get queued
up.
But as Chris points out, that's not true any more. As of the first
service pack for .NET 2.0, the maximum number of active threads in the
default thread pool is 250 per CPU.

I believe that was his point, or at least part of it. I agree that with
the original limit of 25, it's not a problem to throw a bunch of tasks at
the thread pool. But unfortunately the Microsoft folks decided to work
around badly designed applications by raising the limit to 250 from 25.
This creates a significant risk of consuming process address space for a
few applications that assume the limit is lower, have a very large number
of tasks, and use the thread pool for their task queuing.

I guess the lesson here (or at least one of them) is that if you're going
to use a thread pool to handle your task queuing, include code to make
sure that the maximum thread count of threads for the thread pool is
something reasonable. :)

Pete
Dec 10 '07 #25

P: n/a
"Peter Duniho" <Np*********@nnowslpianmk.comwrote
>You can exhaust the threadpool, of course, but that's when items start
to get queued.

That's not what Chris is talking about. He's referring to a design bug in
which code executing in a thread pool thread waits for another thread
executing in a thread pool thread to complete. IMHO, no one should ever
write code like this anyway, but if they do they can create a situation
where all of the thread pool threads are stuck waiting on other threads
(typically threads they created, but that's not important) that have all
been queued and won't execute until a currently running thread pool thread
finishes its task.
The problem is, it's really easy to write this broken code. By mistake.

For example, I first came across this because our methods, running on
threadpool threads, were calling SQL Server.

We happened to hit a case where all 25 threadpool threads were calling into
SQL Server at once. This means, there was no threadpool thread available for
ADO.Net to use, and completed operations just got queued up in the
ThreadPool Work Queue. Meanwhile all 25 threads were waiting for results.
End result: Deadlocked process.

.... the underlying problem is that .Net Internals make use of the threadpool
all over the place. If you've got all the threads busy, the Framework can't
do it's job, and your entire process is at risk for hanging.

This is (in my opinion) easily solved by using a different thread pool for
your own stuff. Jon Skeet's is fine, and Jeff Richter (as part of his Power
Threading library) has a nice Thread Pool implementation.

The new threading stuff in Vista (not .Net, only accessable via Win32) looks
really nice. They unified the IOCP and Process thread pool, and give code
the ability to create additional pools withing the process. Now I just need
them to port that back to XP / Win2k3 and write a bunch of managed code
around it. I'm not holding my breath...

--
Chris Mullins
Dec 10 '07 #26

P: n/a
"Martin Carpella" <ma*************@gmx.netwrote
"Chris Mullins [MVP - C#]" <cm******@yahoo.comwrites:
>If you've got all of the threadpool threads busy, you may end up
deadlocking
the entire threadpool.

You can exhaust the threadpool, of course, but that's when items start
to get queued.
Yea. That's true. ... but there are bugs in the current threadpool
implementation that makes it possible to deadlock the ThreadPool by mistake.

The way I personally discovered this is by doing I/O operations from a
ThreadPool thread. Under load, we had all threadpool threads doing I/O at
the same time, which caused the problem. More detail about my discover of
this can be found at:
http://www.coversant.com/dotnetnuke/...d=88&EntryID=8
Well, you'd have around 25-50 threads on a typical thread pool on a
single/dual-core computer. If you have more tasks, they'll get queued
up.
The threadpool, to help avoid the deadlock bug I'm describing above, was
updated in a Service Pack. The default limit was grown from 25 threads to
250 threads per process.

Details at:
http://www.bluebytesoftware.com/blog...dbd7d2108.aspx

--
Chris Mullins
Dec 10 '07 #27

P: n/a
Peter Duniho <Np*********@nnowslpianmk.comwrote:
The problem is, it's really easy to write this broken code. By mistake.

Well, I don't disagree that's a problem. But I don't think the fix is to
increase the maximum number of threads in the pool by a factor of 10.
It's certainly not an elegant fix, or indeed one that will solve all
problems. However, I suspect that it *is* a pragmatic fix:

1) It will make the problem disappear in a large number of realistic
cases

2) It shouldn't break anything else, including anything assuming
details of what gets run on a threadpool thread.

It's not nice, but it may well be the "least bad" option available at
the moment.

--
Jon Skeet - <sk***@pobox.com>
http://www.pobox.com/~skeet Blog: http://www.msmvps.com/jon.skeet
World class .NET training in the UK: http://iterativetraining.co.uk
Dec 10 '07 #28

P: n/a
"Chris Mullins [MVP - C#]" <cm******@yahoo.comwrote in message
news:OE**************@TK2MSFTNGP03.phx.gbl...
"Martin Carpella" <ma*************@gmx.netwrote
>"Chris Mullins [MVP - C#]" <cm******@yahoo.comwrites:
>>If you've got all of the threadpool threads busy, you may end up
deadlocking
the entire threadpool.

You can exhaust the threadpool, of course, but that's when items start
to get queued.

Yea. That's true. ... but there are bugs in the current threadpool
implementation that makes it possible to deadlock the ThreadPool by
mistake.

The way I personally discovered this is by doing I/O operations from a
ThreadPool thread. Under load, we had all threadpool threads doing I/O at
the same time, which caused the problem. More detail about my discover of
this can be found at:
http://www.coversant.com/dotnetnuke/...d=88&EntryID=8
>Well, you'd have around 25-50 threads on a typical thread pool on a
single/dual-core computer. If you have more tasks, they'll get queued
up.

The threadpool, to help avoid the deadlock bug I'm describing above, was
updated in a Service Pack. The default limit was grown from 25 threads to
250 threads per process.

Chris,

You mean "250 threads per processor" isn't it? Note that the SP you are
talking about is SP1 for the .NET Framework 2, which is part of 3.5 but also
available as a stand-alone download, so it can be applied without the need
to install 3.5.
With this SP we now have a redesigned TP with a default maximum of 250 TP
per processor and 1000 IOCP threads per process.

Willy.

Dec 10 '07 #29

P: n/a
On Mon, 10 Dec 2007 10:53:55 -0800, Jon Skeet [C# MVP] <sk***@pobox.com>
wrote:
Peter Duniho <Np*********@nnowslpianmk.comwrote:
The problem is, it's really easy to write this broken code. By
mistake.

Well, I don't disagree that's a problem. But I don't think the fix is
to
increase the maximum number of threads in the pool by a factor of 10.

It's certainly not an elegant fix, or indeed one that will solve all
problems. However, I suspect that it *is* a pragmatic fix:
Well, given how broadly "pragmatic" can be interpreted, I can't argue with
that. :) But...
1) It will make the problem disappear in a large number of realistic
cases
Yes.
2) It shouldn't break anything else, including anything assuming
details of what gets run on a threadpool thread.
No. As I pointed out, it could easily break an application that assumes
it can send thousands of work items to the thread pool without any real
problems. Even if doing so doesn't actually consume all of the address
space (that would require either an 8-core system, or a 4-core system
along with code that does enough i/o to cause 1000 IOCP threads to be
created), it could easily result in a significant decrease in virtual
address space available for other things.
It's not nice, but it may well be the "least bad" option available at
the moment.
I guess it's all relative. But for a "service pack" level of update, I
would expect something better. It's definitely not true that the change
is harmless.

Pete
Dec 10 '07 #30

P: n/a
"Willy Denoyette [MVP]" <wi*************@telenet.bewrote:
"Chris Mullins [MVP - C#]" <cm******@yahoo.comwrote in message
>The threadpool, to help avoid the deadlock bug I'm describing above, was
updated in a Service Pack. The default limit was grown from 25 threads to
250 threads per process.
You mean "250 threads per processor" isn't it?
Of course. That'll teach me (again!) to not proofread my posts a 3rd or 4th
time.
With this SP we now have a redesigned TP with a default maximum of 250 TP
per processor and 1000 IOCP threads per process.
You mean "250 TP per processor core" isn't it? :)

(I couldn't help myself. I tried. I really did. I just couldn't resist.)

I have wondered for a while, and I suspect you would know, does a
Hyperthreaded processor count as a processor core for this? I've always
assumed it did, but I could really see it either way.

--
Chris Mullins
Dec 10 '07 #31

P: n/a
"Peter Duniho" <Np*********@nnowslpianmk.comwrites:
That's not what Chris is talking about. He's referring to a design bug
in which code executing in a thread pool thread waits for another
thread executing in a thread pool thread to complete.
Ups, sorry, I completely misunderstood this.
IMHO, no one
should ever write code like this anyway, but if they do they can create
a situation where all of the thread pool threads are stuck waiting on
other threads (typically threads they created, but that's not
important) that have all been queued and won't execute until a
currently running thread pool thread finishes its task.
I know, I managed to produce such a bug myself by exhausting the
ThreadPool with BeginInvoke().
But as Chris points out, that's not true any more. As of the first
service pack for .NET 2.0, the maximum number of active threads in the
default thread pool is 250 per CPU.
I see, this particular change slipped my notice and I totally agree with
you regarding to the consequences for the process space in a 32bit process.

Thanks for clarifying my misunderstanding!

Best regards,
Martin
Dec 10 '07 #32

P: n/a
"Chris Mullins [MVP - C#]" <cm******@yahoo.comwrites:
Yea. That's true. ... but there are bugs in the current threadpool
implementation that makes it possible to deadlock the ThreadPool by
mistake.
I noticed this myself, by using BeginInvoke() and several
BackgroundWorkers I managed to exhaust the available threads in the
pool. Changed the complete design afterwards to fix the problem (was a
better solution then, anyway).
The threadpool, to help avoid the deadlock bug I'm describing above, was
updated in a Service Pack. The default limit was grown from 25 threads to
250 threads per process.

Details at:
http://www.bluebytesoftware.com/blog...dbd7d2108.aspx
Thanks for your update. I missed this change. Sorry for argumenting with
outdated information.

Beste regards,
Martin
Dec 10 '07 #33

P: n/a
"Chris Mullins [MVP - C#]" <cm******@yahoo.comwrote in message
news:O4**************@TK2MSFTNGP05.phx.gbl...
"Willy Denoyette [MVP]" <wi*************@telenet.bewrote:
>"Chris Mullins [MVP - C#]" <cm******@yahoo.comwrote in message
>>The threadpool, to help avoid the deadlock bug I'm describing above, was
updated in a Service Pack. The default limit was grown from 25 threads
to 250 threads per process.
>You mean "250 threads per processor" isn't it?

Of course. That'll teach me (again!) to not proofread my posts a 3rd or
4th time.
Happens to me all the time, watch out ;-)
>With this SP we now have a redesigned TP with a default maximum of 250 TP
per processor and 1000 IOCP threads per process.

You mean "250 TP per processor core" isn't it? :)
Processor cores in case of muti-core architectures, or processors in case of
single core legacy archtectures.
(I couldn't help myself. I tried. I really did. I just couldn't resist.)

I have wondered for a while, and I suspect you would know, does a
Hyperthreaded processor count as a processor core for this? I've always
assumed it did, but I could really see it either way.
Yes it does, although it better didn't :-)

Willy.

Dec 10 '07 #34

P: n/a
On Mon, 10 Dec 2007 13:07:07 -0800, Willy Denoyette [MVP]
<wi*************@telenet.bewrote:
[...]
>I have wondered for a while, and I suspect you would know, does a
Hyperthreaded processor count as a processor core for this? I've always
assumed it did, but I could really see it either way.

Yes it does, although it better didn't :-)
I'll second that. I learned the hard way that while hyperthreading can
help somewhat for multi-process situations, and can even help a little for
certain multi-thread situations, it's very easy for a hyperthreading
processor to perform _much_ worse with multiple threads than with just
one. :(

Pete
Dec 10 '07 #35

This discussion thread is closed

Replies have been disabled for this discussion.