I understand that calling Thread.Abort() is BAD AND WRONG because of various reasons
- Can be locking or in other critical region
- Could be midway through an I/O operation, resource won't get cleaned up
- Code can swallow ThreadAbortException
- Could be midway through a state change operation, and leave the program in an invalid state.
Assuming that the thread's Run() method:
- Does not use concurrency primitives.
- Does not perform any I/O.
- Does not swallow such exceptions.
- Is exposed to a read-only API, so cannot change program state.
Question:
If I were to call Abort() on such a thread, is the damage done (with regards to corruption) localized? As in, can I rely on my program, sans the recently aborted thread, to continue to behave normally, or is my entire process/AppDomain/?? then potentially corrupted?
If I remove assumptions 1 and 开发者_StackOverflow2 would abort() corruption be localized to the specific concurrency primitives and i/o resources it accessed? Or can it propogate outwards?
Why am I flogging the Thread.Abort() dead horse:
I'm writing a thick client c# winforms application. One feature s the ability for the user to write code in c# which implements interfaces on my domain objects which can then be plugged into my application (code is compiled into an assembly via codedom and loaded using reflection);
So the idea is the user could then write
public class CustomComputation : IComputationThing
{
public void Compute(object context)
{
while (true) ;
}
}
and I could then reflect on the generated assembly, extract and instantiate an instance of CustomComputation
, and call Compute(...);
Pretty straightforward stuff.
The problem is that the user could then write, well, the above - an infinite loop, or some other exponential time computation he decides he doesn't want to wait for.
I am trying to investigate my options for providing a best-effort recovery for when users write some infinite-looping (or exponential time etc) code and wants to abort from that. While I do intend to ask users to adhere to a cancellation pattern but ultimately there is no guarantee that they will.
As mentioned by Vilx- you could consider putting the "user" code in another AppDomain.
In fact, since .Net 3.5, the .Net Framework has included the System.AddIn
namespace, which provides a simplified way of isolating "add-in" code in seperate app domains (amongst other things). Provided there isn't too much cross-domain communication required, this gives you a nice level of isolation whereby the worst case scenario is that you tear down an AppDomain that is specific to the user code.
See this MSDN article and the CLR-Addin team's blog for more information.
Additionally, although it doesn't solve everything, you could start by firing a Thread.Interrupt. This will cause a ThreadInterruptException to be raised in a similar way to ThreadAbortException, except that it will occur oinly at defined points in the code; when the thread blocks or sleeps, rather than absolutely anywhere. It probably won't help in the tight-loop example you mention above, but would if the loop had a Thread.Sleep(0).
You can always graduate your options:
- Put a Stop() method on IComputationThing and ask users to implement it.
- If an add-in doesn't respond to Stop() in a timely fashion, try a Thread.Interrupt (and document that tight-loopers might want to put a Thread.Sleep(0) in their loops to yield to another thread)
- If that fails, try the Thread.Abort, or tear down the App Domain (probably the latter directly).
Unless you are privy to every detail of the .NET runtime libraries, I doubt you could actually guarantee your assumptions (particularly 4 and 1). All it really takes to violate assumption 4 is a single static variable somewhere...
If you work with generated code, a separate AppDomain is much easier to stop and unload, and unlike threads, this can be done as part of the normal lifecycle of the hosting process.
The Thread.Abort() will certainly taint your AppDomain but I believe it could also destabilize the entire Process. I couldn't find a link for that so quickly.
So you'll have to run it in a separate (disposable) Process.
Since the user code is so dynamic (I expect that the typical developer will do dozens of test runs while writing his code), you'll want to put it in a separate AppDomain anyway so you can unload it. If so, the corruption will then only affect the separate appdomain, which will be unloaded soon anyway.
Now, I'm actually starting to wonder what would happen if the user code would spawn another thread/appdomain, but that's another issue. Perhaps it can be solved with some permissions, I'm not throughly familiar with those.
Have a look at the .net Terrarium project. This is a peer-to-peer demo application written to demonstrate peer-to-peer capabilities and hack-safety of the .net framework. Developers could anonymously write and upload AI code to control insects in a virtual terrarium that was running as a peer to peer ecosystem on thousands of machines.
They used reflection to make sure that the code uploaded was safe: you could not do IO, locks were restricted, as well as static variables. They limited also the time you were allowed to "think". What's good enough for this app should be good enough for you.
http://terrarium2.codeplex.com/
精彩评论