开发者

Why ThreadAbortException does not throw in catch block

Suppose I have this code:

    static void Main(string[] args)
    {
        var thread = new Thread(() =>
        {
            try
            {
                throw new InvalidOperationException();
            }
            catch (Exception)
            {
                Thread.Sleep(Timeout.Infinite);
            }
        });
        thread.Start();

        Thread.Sleep(TimeSpan.FromSeconds(1));

        thread.Abort();
        thread.Join();
    }

It starts thread, then thread is going into sleep in catch block and after that we are trying abort thread.

Abort method have to raise ThreadAbortException. But in catch block it does not happen. It's documented:

The thread that calls Abort might block if the thread that is being aborted is in a protected region of code, such as a catch block, finally block, or constrained execution region. If the thread that calls Abort holds a lock that the aborted thread requires, a deadlock can occur.

My question is why. Why is it working that way? Because in catch block we can raise any exceptions and all works like it have to.

UPDATE: From the link by Jordão. Accepted because it's the most understandable clarification.

Constrained Execution Regions The .NET Framework 2.0 introduces Constrained Execution Regions (CER), which impose restrictions both on the runtime and on the developer. In a region of code marked as a CER, the runtime is constrained from throwing certain asynchronous exceptions that would prevent the region from executing in its entirety. The developer is also constrained in the actions that can be performed in the region. This creates a framework and an enforcement mechanism for authoring reliable managed code, making it a key player in the reliability story for the .NET Framework 2.0. For the runtime to meet its burden, it makes two accommodations for CERs. First, the runtime will delay thread aborts for code that is executing in a CER. In other words, if a thread calls Thread.Abort to abort another thread that is currently executing within a CER, the runtime will not abort the target thread until execution has left the CER. Second, the runtime will prepare CERs as soon as is possible to avoid out-of-memory conditions. This means that the runtime will do everything up front that it would normally do during the code region's JIT compilation. It will also probe for a certain amount of free stack space to help eliminate stack overflow exceptions. By doing this work up front, the runtime can better avoid exceptions that might occur within the region and prevent resources from being cleaned up appropriately. To use CERs effectively, developers should avoid certain actions that might result in asynchronous exceptions. The code is constrained from performing certain actions, including things like explicit allocations, boxing, virtual method calls (unless the target of the virtual method call has already been prepared), method calls through reflection, use of Monitor.Enter (or the lock keyword in C# and SyncLock in Visual Basic®), isinst and castclass instructions on COM objects, field access through transparent proxies, serialization, and multidimensional array accesses. In short, CERs are a way to move any runtime-induced failure point from your code to a time either before the code runs (in the case of JIT compiling), or after the code completes (for thread aborts). However, CERs really do constrain the code you can write. Restrictions such as not allowing most allocations or virtual method calls to unprepared targets are significant, implyi开发者_StackOverflow中文版ng a high development cost to authoring them. This means CERs aren't suited for large bodies of general-purpose code, and they should instead be thought of as a technique to guarantee execution of small regions of code.


The problem is that the thread you're attempting to abort is running inside a catch clause.

This will abort the thread:

static void Main(string[] args) {
  var thread = new Thread(() => {
    Thread.Sleep(Timeout.Infinite);
  });
  thread.Start();

  Thread.Sleep(TimeSpan.FromSeconds(1));

  thread.Abort();
  thread.Join();
}

From this article:

In the .NET Framework 2.0, the CLR delays graceful thread aborts by default over CERs, finally blocks, catch blocks, static constructors, and unmanaged code.

This feature exists to keep the .NET framework more reliable in the face of certain asynchronous exceptions. Read the article I linked for the full story.

Your code basically misbehaves and a host would probably escalate that thread to a rude thread abort:

Rude thread aborts and rude application domain unloads are used by CLR hosts to ensure that runaway code can be kept in check. Of course, failure to run finalizers or non-CER finally blocks due to these actions presents the CLR host with new reliability problems, since there's a good chance these actions will leak the resources the back-out code was supposed to clean up.


This is by design, this was introduced in Fx 3 or 4.
You can look up the different versions form your own link and find different descriptions.

Allowing an AbortException inside those protected regions (as in Fx 1.x) can lead to very unpredictable situations and an unstable Process.

Note that Thread.Abort() is (was) generally dis-advised. And so is long-running code in any catch or finally clause.

Disallowing Abort to interrupt a catch clause addresses some of the issues with Abort. But it's still not perfect.


I suspect the point is that while you're in a catch or finally block, you're probably trying to clean up after yourself already. If an asynchronous exception can be triggered at that point, it's going to be really hard to do any sort of reliable cleanup.

Joe Duffy's blog post about asynchronous exceptions is likely to clarify this more than I can...

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜