How to stop a thread in. Net (and why thread. Abort is edevil)

Source: Internet
Author: User
Tags tidy

Threads are a popular way of getting on with multiple things at once. they are not the only way of doing this, but they are participant uarly attractive if you are faced with an API that is resolutely synchronous.

A common question that emerges once you have kicked off some concurrent work is: how do I stop it? Here are two popular reasons for wanting to stop some work in progress:

    1. You need to shut down the program.
    2. The user canceled the operation.

In the first case, it is often acceptable to drop everything mid flow and not bother shutting down cleanly, because the internal state of the program no longer matters, and the OS will release resources held by our program when it exits. the only concern is if the program stores state persistently-it is important to make sure that any such State is consistent when our program exits. however, if we are relying on a database for such State, we can still often get away with abandoning things mid flow, if we are using transactions-aborting a transaction rolls everything back to where it was before the transaction started, so this shoshould be sufficient to return the system to a consistent state.

There are of course cases where dropping everything on the floor will not work. if the application stores its state on disk without the aid of a database, it will need to take steps to make sure that the on-disk representation is consistent before abandoning an operation. and in some cases, a program may have interactions in progress with external systems or services that require explicit cleanup B Eyond what will happen automatically. however, if you have designed your system to be robust in the face of a sudden failure (e.g. loss of power) then it shoshould be acceptable simply to abandon work in progress rather than cleaning up neatly when shutting the program down. (Indeed there is a school of thought that says that if your program requires explicit shutdown, it is not sufficiently robust- For a truly robust program, sudden termination shoshould always be a safe way to shut down. and given that, some say, you may as well make this your normal mode of shutdown-it's a very quick way of shutting down !)

User-initiated cancellation of a single operation is an entirely different matter however.

If the user chooses to cancel an operation for some reason-maybe it is taking too long-she will keep CT to be able to continue using the program afterwards. it is therefore not acceptable simply to drop everything on the floor, because the OS is not about to tidy up after us. our program has to live with its internal state after the operation has been canceled. it is therefore necessary for cancellation to be done in an orderly fashion, so that the program's state is still internally consistent once the operation is complete.

Bearing this in mind, consider the useThread. Abort. This is, unfortunately, a popular choice for canceling work, because it usually manages to stop the target thread no matter what it was up. this means you will often see its use recommended on mailing lists and news groups as a way of stopping work in progress, but it is really only appropriate if you are in the process of shutting down the program, because it makes it very hard to be sure what state the program will be in afterwards.

Asynchronous exceptions

The problemThread. AbortIs that it can interrupt the progress of the target thread at any point. it does so by raising an 'asynchronous' exception, an exception that coshould emerge at more or less any point in your program. (This has nothing to do with. net async pattern by the way-that's about doing work without hogging the thread that started the work .)

Most exceptions are synchronous, meaning that it is possible to determine the points in a program at which such an exception might be thrown. For example, when you callSystem. int32.parseYou know that it will throwFormatexceptionIf there is something wrong with the string you pass it. most importantly you know that it won't wait until you 've executed a few lines of code before saying "Oh by the way, here's an exception. "If the callInt32.parseReturns normally, you know that you won't be seeingFormatexception.

With Asynchronous exceptions on the other hand, you never know where they might emerge-They cocould be thrown at more or less any point in your program's execution. this makes them rather hard to deal with-how are you supposed to fetch gracefully with exceptions if you have no idea where they will emerge?

This is a particle ly big problemFinallyBlocks. If you're doing your exception handling properly, you'll most likely have far moreFinally(OrUsing) Blocks in your code than you haveCatchBlocks. this is because in order to recover successfully from an error, your code will need to tidy up after itself. and since C # doesn't support C ++-style scope-based destructor execution,FinallyBlocks (and their close cousins,UsingBlocks) are the only sane way of ensuring that such tidying is saved med reliably.

Consider this Code:

 
1: Using(Filestream FS = file. Open (mydatafile,

 
2:Filemode. Open, fileaccess. readwrite, fileshare. None ))

 
3:{

 
4:...DoStuff with data file...

 
5:}

ThisUsingBlock is really shorthand for this:

 
1:Filestream FS = file. Open (mydatafile,

 
2:Filemode. Open, fileaccess. readwrite, fileshare. None );

 
3: Try

4:{

 
5:...DoStuff with data file...

 
6:}

 
7: Finally

 
8:{

 
9:Idisposable disp = FS;

 
10:Disp. Dispose ();

11:}

The compiler will generate thatFinallyBlock for us. (We coshould write it out in full like this every time, we just don't usually bother, because the first example is much more succinct and easier to read .) the whole idea ofUsingStatement here is that it guarantees to close the file regardless of whether we leaveUsingBlock normally, or by throwing an exception.

Asynchronous exceptions weaken this guarantee.

Suppose the code above will be working on the file for some time, and you 've decided to do it on some worker thread. now suppose the user has chosen to cancel the operation, and your UI thread CILSThread. AbortTo stop the operation. Most of the time, this will be actually work. However, there's one situation in which it goes horribly wrong.

suppose the worker thread had very nearly finished when the user decided to abort the operation. what happens if the worker thread has just entered the compiler-generated finally block when the UI thread CILS thread. abort ? If the worker thread is now in the finally block, it is outside of the try block. this means that if the threadabortexception gets raised at this point, the remainder of the finally block won't run to completion. and if the worker thread HADN' t quite managed to call dispose yet, or it had but the filestream object hadn't quite managed to close the file yet, the file isn't going to get closed.

At best,Filestream'S finalizer will eventually run and close the file. But it's conceivable thatFilestream. DisposeMethod might set its internal State to indicate that the file has been closed before it really closes the handle. most classes aren't written to behave predictably if you start injecting asynchronous exceptions onto the thread you're calling their methods on.

The bottom line is that if an asynchronous exception occurs at the wrong moment, the file will remain open, possibly until the process exits. since the file was opened for exclusive access, this means further attempts to open the file will fail until the process exits. the user will learn to hate your program.

This kind of thing is what makes async exceptions edevil. And sinceThread. AbortWorks by raising an asynchronous exception, we can conclude thatThread. AbortIs edevil. Hence the title.

Non-edevil Cancellation

SinceThread. AbortIs bad news, how shoshould we cancel operations? I think that if you're looking at how to do something to the worker thread to stop it, you're looking at it from the wrong angle. (So I wouldn't recommendThread. InterruptEither, although at least it doesn' t raise exceptions asynchronously .)

The approach I always recommend is dead simple. HaveVolatile boolField that is visible both to your worker thread and your UI thread. if the user clicks cancel, set this flag. meanwhile, on your worker thread, test the flag from time to time. if you see it get set, stop what you're re doing.

The issue most people initially have with this approach is that it doesn't forcibly stop the thread in whatever it's in the middle. that's actually a good thing though-it's much easier to keep your program's internal state consistent if you get to choose when to abort an operation. and in any case, if you're concerned about how long it will take for an operation to grind to a halt, just pretend to the user that it has been canceled as soon as they click Cancel, and then let it grind to a halt on the worker thread in its own sweet time. of course, in some scenarios you will actually need to make the user wait until you 've managed to stop the operation, but in the case where there's no good reason to do this, just relax and let things come to a halt in their own time.

Alternative non-edevil Cancellation

There is a completely different approach you can take: use processes. if you fire up an entirely seperate process to do the background work, then you can nuke it with impunity, because you don't care about its internal state, and it won't affect your process's internal state. (although if it modifies persistent state you still need to take care to leave it in a persistent state .)

It feels pretty stone-age if you're used to using multiple threads, because it's so much effort extends alling data into and out of the remote process, but it is a workable approach. it also allows the child process to be incredibly shabby about releasing resources because it knows it's not going to live long. the main problem is that processes are relatively heavyweight on Windows. so for those two reasons, I tend to prefer the in-process solution.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.