I need to develop a multithreaded Azure worker role in C# - create multiple threads, feed requests to them, each request might require some very long time to process (not my code - I'll call a COM object to do actual work).
Upon role shutdown I need to gracefully stop processing. How do I do that? Looks like if I just call Thread.Abort()
the ThreadAbortException
is thrown in the thread and the thread can even use try-catch-finally
(or using
) to clean up resources. This looks quite reliable.
What bothers me is that my experience is mostly C++ and it's impossible to gracefully abort a thread in an unmanaged application - it will just stop without any further processing and this might leave data in inconsistent state. So I'm kind of paranoid about whether anything like that happens in case I call Thread.Abort()
for a busy thread.
Is it safe practice to use Thread.Abort()
together with handling ThreadAbortException
? What should I be aware of if I 开发者_如何学Godo that?
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.
One example where it's problematic to abort a thread.
using(var disposable=new ClassThatShouldBeDisposed())
{
...
}
Now the Thread abortion happes after the constructor of the class has finished but before the assignment to the local variable. So it won't be disposed. Eventually the finalizer will run, but that can be much later.
Deterministic disposing and thread abortion don't work well together. The only way I know to get safe disposing when using thread abortion is putting all the critical code inside a finally
clause.
try
{//Empty try block
}
finally
{
//put all your code in the finally clause to fool thread abortion
using(var disposable=new ClassThatShouldBeDisposed())
{
...
}
}
This works because thread abortion allows finally
code to execute. Of course this implies that the thread abortion will simply not work until the code leaves the finally
block.
One way to get your code to work correctly with thread abortion is using the following instead of the using
statement. Unfortunately it's very ugly.
ClassThatShouldBeDisposed disposable=null;
try
{
try{}finally{disposable=new ClassThatShouldBeDisposed();}
//Do your work here
}
finally
{
if(disposable!=null)
disposable.Dispose();
}
Personally I just assume threads never get aborted(except when unloading the AppDomain) and thus write normal using
based code.
It's very difficult to handle the TheadAbortException
correctly, because it can be thrown in the middle of whatever code the thread is executing.
Most code is written with the assumption that some actions, for example int i = 0;
never causes an exception, so the critical exception handling is only applied to code that actually can cause an exception by itself. When you abort a thread, the exception can come in code that is not prepared to handle it.
The best practice is to tell the thread to end by itself. Create a class for the method that is running the thread, and put a boolean variable in it. Both the code that started the thread and the method running the thread can access the variable, so you can just switch it to tell the thread to end. The code in the thread of course have to check the value periodically.
Thread.Abort
is an unsafe way of killing the thread.
- It rises an asynchronous
ThreadAbortException
which is a special exception that can be caught, but it will automatically be raised again at the end of the catch block - It can leave the state corrupted, and your application becomes unstable
- TAE is raised in the other thread
The best practise is to use wrappers that support work cancellation, such as the Task
class or use volatile
bool. Instead of Thread.Abort
consider using Thread.Join
which will block the calling thread until the working thread is disposed of.
Some links:
- How To Stop a Thread in .NET (and Why Thread.Abort is Evil)
- Managed code and asynchronous exception hardening
- The dangers of Thread.Abort
As others have mentioned, aborting a thread is probably not a good idea. However, signalling a thread to stop with a bool may not work either, because we have no guarantee that the value of a bool will be synchronized across threads.
It may be better to use an event:
class ThreadManager
{
private Thread thread = new Thread(new ThreadStart(CallCOMMethod));
private AutoResetEvent endThread = new AutoResetEvent(false);
public ThreadManager()
{
thread.Start();
}
public StopThread()
{
endThread.Set();
}
private void CallCOMMethod()
{
while (!endThread.WaitOne())
{
// Call COM method
}
}
}
Since the COM method is long running you may just need to "bite the bullet" and wait for it to complete its current iteration. Is the information computed during the current iteration of value to the user?
If not, one option my be:
- Have the ThreadManager itself run on a separate thread from the UI which checks for the stop notification from the user relatively often.
- When the user requests that the long running operation be stopped, the UI thread can immediately return to the user.
- The ThreadManager waits for the long running COM operation to complete its current iteration, then it throws away the results.
It's considered best practice to just let the thread's method return:
void Run() // thread entry function
{
while(true)
{
if(somecondition) // check for a terminating condition - usually "have I been disposed?"
break;
if(workExists)
doWork();
Thread.Sleep(interval);
}
}
Please get simple idea from here as for your requirement, check thead isalive property, then abort your thread.............................................................
ThreadStart th = new ThreadStart(CheckValue);
System.Threading.Thread th1 = new Thread(th);
if(taskStatusComleted)
{
if (th1.IsAlive)
{
th1.Abort();
}
}
private void CheckValue() { //my method.... }
精彩评论