Do not suspend or abort threads

来源:互联网 发布:云计算世界排名 编辑:程序博客网 时间:2024/06/06 23:51

I’ve said a number of times that calling Thread.Abort is like stopping a car by shooting the driver in the head. The car will stop, but there’s no telling what damage it’ll do in the process.

Calling Thread.Abort throws an exception that will break execution. You can catch ThreadAbortException and even prevent it from killing your thread, but you can’t prevent the thread abort from breaking execution. For example, say you have a thread that’s executing this method:

    void MyThreadProc(object state)    {        while (true) // go forever        {            // process an item        }        Console.WriteLine("Thread exited normally.");    }

If the main thread calls Abort on that thread, the thread will terminate. Whatever the thread was doing is aborted and the thread exits the method without writing anything to the console.

The first and most important problem here is that when you call Abort, whatever is happening in that loop is interrupted. Your program could be in the middle of a critical multi-part data update, could be holding a mutex, have allocated critical system resources, etc. When the thread aborted, the update is left unfinished. Unless you’re religious about using try/finally, any mutex you held continues to be held, critical system resources aren’t released, etc. Even if you do clean up allocated resources, you still have the problem of an unfinished data update. Your program is left in an unknown and therefore potentially corrupt state from which you can’t reliably recover.

Catching ThreadAbortException helps in that you can probably structure your code so that the exception handler does some cleanup and even notifies you that the thread was terminated unexpectedly.

    void MyThreadProc(object state)    {        try        {            while (true) // go forever            {                // process an item            }            Console.WriteLine("Thread exited normally.");        }        catch (ThreadAbortException)        {            Console.WriteLine("Thread aborted.");            // do cleanup here        }        Console.WriteLine("End of thread.");    }

Now if you abort the thread, the catch block is executed. Still, whatever the thread was doing inside the loop is interrupted. And ThreadAbortException is special. Unlike most other exceptions, which are done once you catch them, ThreadAbortException is re-thrown by the runtime. You would never see the “End of thread” message here if the thread is aborted.

The discussion above should disabuse you of the notion that depending on Thread.Abort is a good idea. But let’s say that you know your thread isn’t doing any of those potentially dangerous things, so you think you’re safe.

Then comes the next problem: you can’t guarantee that Thread.Abort will actually abort the thread. The thread could catch ThreadAbortException and then call Thread.ResetAbort to prevent the exception from being re-thrown. That prevents Thread.Abort from terminating the thread. For example:

    void MyThreadProc(object state)    {        while (true)        {            try            {                while (true) // go forever                {                    // process an item                }                Console.WriteLine("Thread exited normally.");            }            catch (ThreadAbortException)            {                Console.WriteLine("No! I'll stop when I'm good and ready.");                Thread.ResetAbort();            }        }        Console.WriteLine("End of thread.");    }

That doesn’t prevent Abort from interrupting the loop, though. Even with >ResetAbort, whatever is happening in the inner loop is vulnerable. You can’t prevent Abort from interrupting the thread.

Don’t make the mistake of thinking that surrounding your critical code with calls to Thread.BeginCriticalRegion and Thread.EndCriticalRegion will solve the problem. Those might prevent the runtime host from throwing ThreadAbortException, true. It might instead just tear down the app domain, terminating the entire program. Oops.

It should be clear by now that calling Thread.Abort is either dangerous or useless. There is no good reason to use it. Use cancellation if you need the ability to terminate threads. That way, the thread cooperates in the shutdown and nothing critical gets interrupted; the thread can exit when it is safe to do so.

As bad as Thread.Abort is, Thread.Suspend is even worse. You can potentially write code that gracefully handles a thread abort because all relevantfinally blocks are executed when ThreadAbortException is thrown. The aborted thread could have finally blocks that free allocated resources, release locks, etc. But Thread.Suspend? That stops a thread in its tracks. The thread just halts. Instantly. Whatever it was doing is suspended until some code callsThread.Resume. There is no unwinding of the stack or executing finally blocks. The. thread. just. stops. So locks are held, resources remain allocated, updates remain “in progress,” etc. The thread is frozen in time.

That’s never a good idea.

There are much safer ways to make threads suspend processing. Perhaps the easiest is to implement something similar to cancellation, using aManualResetEventSlim. For example:

    ManualResetEvent ContinueFlag = new ManualResetEventSlim(true);    void MyThreadProc(object state)    {        while (ContinueFlag.Wait())        {            // process        }    }

The call to Wait will block until something sets the event. The event remains set until some code resets it. If you want to pause the thread, simply callContinueFlag.Reset. The thread will finish the current iteration of the loop, and then block on the call to Wait until some other thread callsContinueFlag.Set.

If you want to support cancellation as well as pausing, you can pass the cancellation token to Wait, like this:

   try    {        while (ContinueFlag.Wait(cancelToken))        {            // process        }    }    catch (OperationCancelledException)    {        Console.WriteLine("Cancellation requested.");    }

Again, the thread participates in the cancellation, so whatever is happening in the loop is not affected by the cancellation.

If the continue flag is a ManualResetEvent, you’ll have to use a technique similar to that described in How to: Listen for Cancellation Requests That Have Wait Handles.

Your best bet is to forget that Thread.Abort, Thread.Suspend, and Thread.Resume even exist. I can think of very few good uses for Abort (think of it as thegoto of multithreaded programming), and no good use for Suspend and Resume. Don’t use them. If you do, you’ll regret it.


Copy from http://blog.mischel.com/2013/09/16/do-not-suspend-or-abort-threads/

0 0