Delegate BeginInvoke and ManualResetEvent.WaitOne()

  • Thread starter Thread starter Alphapage
  • Start date Start date
A

Alphapage

Hello,

Here is an example:

ManualResetEvent waiter;
delegate DoWorkDelegate;

void DoWork()
{
waiter.WaitOne();
}

void SubMethodnvoke()
{
DoWorkDelegate.BeginInvoke(DoWork);
}

I do something like this when I open Submethods in my app.
I want to know how many submethods I can open.
I think this asynchronous delegate works as a ThreadPool, so if I open more
than the pool threads limit (ie 25 threads by default) I will probably have
some troubles.

In fact, I don't know how it works in background. In the msdn documentation,
ManualResetEvent.WaitOne() blocks the Thread until it receives a signal. But
I hope this is not really the case and the Thread is reused in background to
run some other jobs because if I have more than 25 waiting threads in the
queue my app is dead.
Is ManualResetEvent.WaitOne() really blocking a Thread or is there any job
in the ThreadPool to let those waiting Threads not block all others in the
queue ?

Thanks in advance for your help.
 
Here is an example:

ManualResetEvent waiter;
delegate DoWorkDelegate;

void DoWork()
{
waiter.WaitOne();
}

Don't do that.
void SubMethodnvoke()
{
DoWorkDelegate.BeginInvoke(DoWork);
}

I do something like this when I open Submethods in my app.
I want to know how many submethods I can open.
I think this asynchronous delegate works as a ThreadPool, so if I open
more
than the pool threads limit (ie 25 threads by default) I will probably
have
some troubles.

Yes. Given the above, you certainly could have trouble.
In fact, I don't know how it works in background. In the msdn
documentation,
ManualResetEvent.WaitOne() blocks the Thread until it receives a signal.

That's correct.
But
I hope this is not really the case and the Thread is reused in
background to
run some other jobs because if I have more than 25 waiting threads in the
queue my app is dead.

Unfortunately, hoping for something doesn't make it true. .NET does not
release a thread pool thread back to the pool just because your code has
called WaitHandle.WaitOne(). The thread will indeed simply block and
remain unavailable for further processing.

The thread pool is significantly larger than 25 threads in .NET 2.0 and
later. But, you are right...there is still the potential for a problem.
You'd just need a lot more waiting threads to run into it with more recent
versions of .NET.

In general, it's not a good idea to block in a thread pool thread. Thread
pool threads are for reasonably short tasks that can be completed straight
through. If you need a thread that can block for an arbitrarily long
amount of time, create a new thread for that purpose rather than using the
thread pool.
Is ManualResetEvent.WaitOne() really blocking a Thread or is there any
job
in the ThreadPool to let those waiting Threads not block all others in
the
queue ?

The former. The code you posted is, at least at its most basic, a good
example of how one can deadlock the thread pool. If all those thread pool
threads are all blocked waiting on something to happen, and that something
isn't going to happen until a newly assigned thread pool task executes,
then since that new task will never execute, you'll never get anywhere.

You can correct this by changing the design so that either you have some
way to unblock the threads without running a new thread pool task, or you
simply move the blocking threads out of the thread pool altogether.

A third alternative would be to make sure you never consume the thread
pool completely with threads that could block in that way. That third
solution isn't quite as reliable, since you could have other consumers of
the thread pool doing the same sort of thing, resulting in all the
consumers still managing to consume the thread pool entirely, without any
threads that would unblock the threads being allowed to run. So yet
another alternative would be to implement your own thread pool, one that
you know is used only for this purpose so that you can reliably limit the
number of potentially blocking threads, ensuring you've always got at
least one thread available that can unblock the blocking threads.

Of course, if all of your threads can theoretically be both blocking and
unblocking threads, and if you have no way to determine which they are
prior to executing them, this third solution isn't a solution at all. :)

Yet another way to fix it would be to change the design dramatically,
providing a way for threads that would otherwise have blocked to simply
exit altogether, saving some sort of state so that you can resume
executing whatever they were doing at some later point in time when the
blocking condition is resolved. That'd be a lot more complicated though.
:)

Pete
 
Alphapage said:
Here is an example:

ManualResetEvent waiter;
delegate DoWorkDelegate;

void DoWork()
{
waiter.WaitOne();
}

void SubMethodnvoke()
{
DoWorkDelegate.BeginInvoke(DoWork);
}
Use ThreadPool.RegisterWaitForSingleObject() instead. This way you don't
consume any worker threads until the object is actually signaled, and if the
thread pool is full, it will simply postpone handling until threads become
available.

Even this is not particularly efficient if you're handling a lot of events
this way, because some wait threads must still be occupied with the waiting
(one wait thread can wait on multiple objects, but this doesn't scale
forever). Try to leverage asynchronous programming patterns more. Instead of
waiting on an event, use or write a function that will call a callback when
the work is done.

If you must use synchronous processing (because the code isn't yours and you
can rewrite anything, for example) try to put a limit to the number of
simultaneous outstanding calls yourself. Do not rely on the thread pool to
stall when it runs out of wait threads, because you're in big trouble by
that point.
 
Thanks everyone for those great explainations.
You perfectly answer my questions.

Another little interrogation comes in my mind:
Does the machine free a thread in background if you use WaitOne() or
Sleep(1000000) ? (It doesn't seem to do.)
I can't find any doc and I'm afraid if a thread really sleeps during 1000000
and is not free by the system. It seems to me to be a really bad management
of the ThreadPool threads, doesn't it ?
If I run more waiting threads than the pool threads limit, my app is dead.
 
Thanks everyone for those great explainations.
You perfectly answer my questions.

Another little interrogation comes in my mind:
Does the machine free a thread in background if you use WaitOne() or
Sleep(1000000) ? (It doesn't seem to do.)

Well, I already wrote that using WaitOne() does not free the thread. So
you should already know the answer to that part of your question. As it
happens, there are _no_ blocking methods that would "free a thread in
background" by calling it. The only thing that will return a thread to
the thread pool is to exit from the delegate used to start executing that
thread.

So, no...the thread isn't freed if you call Sleep() either.
I can't find any doc and I'm afraid if a thread really sleeps during
1000000
and is not free by the system.

You should be afraid. Don't sleep for any amount of time, and especially
not 1000 seconds, and especially not in a thread pool thread.

Once in a rare while you might come across a situation in which calling
Sleep() from a non-thread-pool thread is required. If you find yourself
thinking you need to do that in a thread pool thread, you've designed
yourself into a corner and need to fix your design instead of exposing
yourself to potential deadlocks.
It seems to me to be a really bad management
of the ThreadPool threads, doesn't it ?

Yes, it would be bad management on your part of your use of thread pool
threads.
If I run more waiting threads than the pool threads limit, my app is
dead.

That's right. Don't do that.

Pete
 
Thank you very much.
You show me what I mustn't do and give me the right way.
Perfect.
 
Back
Top