Avoiding deadlock caused by Invoke

  • Thread starter Thread starter Mark
  • Start date Start date
M

Mark

I’ll try to explain my situation as clearly as possible… J



I have a UI form that fires a worker thread. The worker thread regularly
updates the UI via an Invoke (naturally). So far so good. The UI form also
has a button that can abort the thread. What I’m trying to achieve is for
the UI to say that the thread has been aborted once it has indeed been
aborted. Here’s my problem:



The user hits the Abort button which does an Abort() on the thread (after
interrupting a waitsleepjoin) and, as per the MSDN documentation then does a
join() to wait for the worker thread to finish (the documentation says that
I *must* join). The worker thread captures the abort signal and attempts to
update the UI via an Invoke. Of course, the main UI thread is now already
joined to the worker thread, so the invoked method called by the worker
thread sits in the UI thread queue and is unable to proceed because the UI
is waiting for the worker thread to abort. Result à deadlock.



I’m sure that there’s a “standard” way to resolve this presumably common
scenario, but I just can’t seem to track it down. Any ideas?



TIA

Mark
 
Mark said:
I=3Fll try to explain my situation as clearly as possible=3F J

I have a UI form that fires a worker thread. The worker thread regularly
updates the UI via an Invoke (naturally). So far so good. The UI form also
has a button that can abort the thread. What I=3Fm trying to achieve isfor
the UI to say that the thread has been aborted once it has indeed been
aborted. Here=3Fs my problem:

The user hits the Abort button which does an Abort() on the thread (after
interrupting a waitsleepjoin) and, as per the MSDN documentation then does a
join() to wait for the worker thread to finish (the documentation says that
I *must* join). The worker thread captures the abort signal and attempts to
update the UI via an Invoke. Of course, the main UI thread is now already
joined to the worker thread, so the invoked method called by the worker
thread sits in the UI thread queue and is unable to proceed because the UI
is waiting for the worker thread to abort. Result à deadlock.

I=3Fm sure that there=3Fs a =3Fstandard=3F way to resolve this presumably common
scenario, but I just can=3Ft seem to track it down. Any ideas?

Yup - don't call Join anywhere, and don't call Abort anywhere. Use an
orderly shutdown instead:

http://www.pobox.com/~skeet/csharp/threads/shutdown.shtml

Alternatively, you could always use BeginInvoke instead of Invoke, but
the above is a better solution. (You may want to use BeginInvoke
anyway, unless you really need to wait for the UI update before
continuing.)
 
your flaw is visible in the description: don't have the worker thread call
Invoke when it captures an abort.

If you get an abort, simply end the thread. Let the U/I, which issued the
abort, handle updates to the U/I.

--- Nick
 
Nick Malik said:
your flaw is visible in the description: don't have the worker thread call
Invoke when it captures an abort.

If you get an abort, simply end the thread. Let the U/I, which issued the
abort, handle updates to the U/I.

Thanks Jon and Nick for your responses. Interesting suggestions. If I
understand correctly, we've got three ideas here:

1. Don't use abort (or indeed join), but use a signalling flag to indicate
that the worker thread should stop. Have the worker thread check this flag
at each iteration and shutdown as appropriate. This looks like a nice clean
solution. The problem that I'm having is that the worker thread has a stage
that could take many seconds (i.e. too long for the user) before checking
the flag. This could leave the UI saying "worker thread stopping..." for too
long. I suppose it might be possible to recode the worker to recheck the
stop flag at more regular intervals, but I'm dependent on waiting for the OS
to time out on open files, so that might not be practical (and the code can
get messy).

2. Don't invoke from the worker thread, but let the UI thread handle the
update to the UI instead. Interesting idea: it means that the UI will have
to monitor and wait for the thread to finish. (A sort of join but without
using join?) I did read somewhere that joins should be avoided in UI threads
as they can theoretically (i.e. with buggy code in the worker) cause the UI
to hang. Thoughts? To me it does seem more logical to have the worker thread
telling the UI what it is doing - including stopping naturally and aborting
than have the UI thread reporting it. I'll think about this more.

3. Use BeginInvoke from the worker thread. This worked a treat! Indeed this
is the idea on MSDN where the author (Chris Sells) states that you should
use BeginInvoke rather than Invoke even from worker threads as it reduces
the chances of a deadlock (though he doesn't use endinvoke even though those
nice chaps in the .net framework runtime department have reserved the right
to be leaky without an endinvoke!). To me, this leads to an interesting
conceptual thought... The UI thread is sitting at a join waiting on the
worker thread. The worker thread puts a UI update message onto the UI thread
via a BeginInvoke but then carries on to exit. The UI thread continues past
the join and the UI update message gets handled by the UI thread after the
worker thread has stopped. This may be "normal" practice, but to me it's
sort of strange that the thread closes before the UI message that says "i'm
closing..." gets to the UI! Probably just academic interest. :-)

So where does this leave me? Well I have solutions, none of which fill me
with a sense of Nirvana but which do nevertheless work. :-) I do like the
idea of not using aborts if possible and using some signalling flag instead,
but I do want to guarantee that I can close the thread after a predefined
timeout should something go wrong in the worker thread as its closing. That
is, trying a tidy shutdown but resorting to an abort if necessary. Looks
like a manualresetevent and waitone might help me here?

Thanks again
Mark
 
Mark said:
3. Use BeginInvoke from the worker thread. This worked a treat! Indeed this
is the idea on MSDN where the author (Chris Sells) states that you should
use BeginInvoke rather than Invoke even from worker threads as it reduces
the chances of a deadlock (though he doesn't use endinvoke even though those
nice chaps in the .net framework runtime department have reserved the right
to be leaky without an endinvoke!).

You don't need to call EndInvoke for calls to Control.BeginInvoke, even
though you do for general BeginXXX/EndXXX calls. The WinForms team has
said they *won't* leak things here.

So where does this leave me? Well I have solutions, none of which fill me
with a sense of Nirvana but which do nevertheless work. :-) I do like the
idea of not using aborts if possible and using some signalling flag instead,
but I do want to guarantee that I can close the thread after a predefined
timeout should something go wrong in the worker thread as its closing. That
is, trying a tidy shutdown but resorting to an abort if necessary. Looks
like a manualresetevent and waitone might help me here?

I don't think you need a ManualResetEvent here - just start a timer
which you either cancel or ignore if the orderly shutdown has worked.
 
Jon Skeet said:
You don't need to call EndInvoke for calls to Control.BeginInvoke, even
though you do for general BeginXXX/EndXXX calls. The WinForms team has
said they *won't* leak things here.

Interesting. I found the reference on MSDN:
http://msdn.microsoft.com/library/d...html/cpovrasynchronousprogrammingoverview.asp
Admitedly, it doesn't say that if you _don't_endinvoke it'll leak, which
kind of begs the question why the MDSN caution says what it does. I did read
somewhere, could be bogus though, that someone had tested begininvokes
without endinvokes and discovered a leak. Oh well, c'est la via.
I don't think you need a ManualResetEvent here - just start a timer
which you either cancel or ignore if the orderly shutdown has worked.

Go point, thanks very much. I'll give it a go.
 
Mark said:
Interesting. I found the reference on MSDN:
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpgu
ide/html/cpovrasynchronousprogrammingoverview.asp
Admitedly, it doesn't say that if you _don't_endinvoke it'll leak, which
kind of begs the question why the MDSN caution says what it does. I did read
somewhere, could be bogus though, that someone had tested begininvokes
without endinvokes and discovered a leak. Oh well, c'est la via.

Yes, but that's talking about calling BeginInvoke on a delegate not a
control.

See the comments in
http://weblogs.asp.net/cbrumme/archive/2003/05/06/51385.aspx
 
Back
Top