Is using Thread.Abort() and handling ThreadAbortException in .NET safe practice?
TL;DR version: No, isn't.
Generally you're safe when all type invariants (whether explicitly stated or not) are actually true. However many methods will break these invariants while running, only to reach a new state when they are again true at the end. If the thread is idle in a state with invariants held you'll be OK, but in that case better to use something like an event to signal the thread to exit gracefully (ie. you don't need to abort).
An out-of-band exception1 thrown in a thread while in such a invariants-not-true, ie. invalid, state is where the problems start. These problems include, but are certainly not limited to, mutually inconsistent field and property values (data structures in an invalid state), locks not exited, and events representing "changes happened" not fired.
In many cases it is possible to deal with these in clean up code (eg. a finally
block), but then consider what happens when the out-of-band exception occurs in that clean up code? This leads to clean up code for the clean up code. But then that code is it self vulnerable so you need clean up code for the clean up code of the clean up code… it never ends!
There are solutions, but they are not easy to design (and tends to impact your whole design), and even harder to test—how to re-create all the cases (think combinatorial explosion). Two possible routes are:
Work on copies of state, update the copies and then atomically swap current for new state. If there is an out-of-band exception then the original state remains (and finalisers can clean up the temporary state).
This is rather like the function of database transactions (albeit RDBMSs work with locks and transaction log files).
It is also similar to the approaches to achieving the "strong exception guarantee" developed in the C++ community in response to a paper questioning if exceptions could ever be safe (C++ of course has no GC/finaliser queue to clean up discarded objects). See Herb Sutters "Guru of the Week #8: CHALLENGE EDITION: Exception Safety" for the solution.
In practice this is hard to achieve unless your state can be encapsulated in a single reference.
Look at "Constrained Execution Regions", but not the limitations on what you can do in these cases. (MSDN Magazine had an introductory article (introduction to the subject, not introductory level), from .NET 2 beta period2).
In practice if you have to do this, using approach #2 to manage the state change under #1 is probably the best approach, but getting it right, and then validating that it is correct (and the correctness is maintained) is hard.
Summary: It's a bit like optimisation: rule 1: don't do it; rule 2 (experts only): don't do it unless you have no other option.
1 A ThreadAbortException
is not the only such exception.
2 So details have possibly changed.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…