.NET Thread.Abort again
Again I want to talk about safety of the Thread.Abort
function. I was interested to have some way to abort operations which I can't control really and don't want actually, but I want to have my threads free as soon as possible to prevent thread thirsty of my application.
So I wrote some test code to see if it's possible to use Thread.Abort
and have the aborting thread clean up resources propertly. Here's code:
int threadRunCount = 0;
int threadAbortCount = 0;
int threadFinallyCount = 0;
int iterations = 0;
while( true )
{
Thread t = new Thread( () =>
{
threadRunCount++;
try
{
Thread.Sleep( Random.Next( 45, 55 ) );
}
catch( ThreadAbortException )
{
threadAbortCount++;
}
finally
{
threadFinallyCount++;
}
} );
t.Start();
Thread.Sleep( 45 );
t.Abort();
iterations++;
}
So, so far this code worked for about 5 mins, and threadRunCount
was always equal to threadFinally
and threadAbort
was somewhat lower in number, because some threads completed with no abort or probably got aborted in finally.
So the question is, do 开发者_运维技巧I miss something?
With a contrived test, you can prove anything.
All you have proved is that with the code you wrote for your test, Thread.Abort
seems to work fine.
The problem is, however, that as soon as you start using things that needs to be disposed of, all hope is lost.
For instance, try this code:
using (Stream stream = new FileStream(@"C:\Test.txt", FileMode.Create, FileAccess.ReadWrite, FileShare.None))
{
Thread.Sleep( Random.Next( 45, 55 ) );
}
Now, run this for a while and tell me if that still works.
The problem appears when the code has left your sleep-call, and is inside the implicit finally-block for the using-block, and is just about to close your stream, and then you abort it.
The problem with Thread.Abort
is that it can happen anywhere, even within code that isn't supposed to throw exceptions.
For instance, do you really expect the following code to crash after the if-expression has been evaluated, but before the Dispose
-call has gone through?
if (_ObjectToDispose != null)
{
_ObjectToDispose.Dispose();
_ObjectToDispose = null;
}
What if it happens right after the call to .Dispose
? The field will still have a non-null value, which could lead to subtle problems elsewhere.
What if you do this:
IDisposable objectToDispose = Interlocked.Exchange(ref _ObjectToDispose, null);
if (objectToDispose != null)
objectToDispose.Dispose();
With this code, you grab the value, replaces it with null, and then before you get around to calling Dispose, your ThreadAbortException
happens, which will just leave the object.
Let me just drive the point home:
Thread.Abort should never be used, except in scenarios where you need to terminate the program (or tear down a custom AppDomain with threads running in it). You should never call Thread.Abort and then continue running.
Unless you need to plan bugs into your future schedule. In that case, go right ahead and use Thread.Abort
, because I can almost guarantee you will have problems.
Using thread abort is safe enough. However as mentioned by others that a thread abort may not get aborted immediately. Calling thread abort will raise a ThreadAbortException in the thread. To clean up resources you can catch this exception and do the necessary cleanups.
static void Run()
{
try
{
while(someCondition)
{
....
....
....
if (someOtherCondition)
throw new ThreadAbortException("Thread aborted");
}
}
catch(ThreadAbortException e)
{
...
... //clean up resources here.
...
}
finally
{
...
}
}
Using Thread.Abort is fine. However, it does not always abort immediately. If a thread is doing unmanaged code, it won't actually abort until it returns to managed code.
精彩评论