[...]
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.
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.
Hey, no drifting back to the original topic, that's bad UseNet form!
Sorry. I forgot the rule of primary Usenet entropy.
Pete