gpt4 book ai didi

c# - 可以解释 PrepareConstrainedRegions 和 Thread.Abort 的这种意外行为吗?

转载 作者:太空狗 更新时间:2023-10-29 23:23:19 26 4
gpt4 key购买 nike

我在玩弄 Constrained Execution Regions今晚更好地完善我对细节的理解。我以前偶尔使用过它们,但在那些情况下,我大多严格遵守既定模式。不管怎样,我注意到了一些我无法解释的奇怪现象。

考虑以下代码。请注意,我以 .NET 4.5 为目标,并使用未附加调试器的发布版本对其进行了测试。

public class Program
{
public static void Main(string[] args)
{
bool toggle = false;
bool didfinally = false;
var thread = new Thread(
() =>
{
Console.WriteLine("running");
RuntimeHelpers.PrepareConstrainedRegions();
try
{
while (true)
{
toggle = !toggle;
}
}
finally
{
didfinally = true;
}
});
thread.Start();
Console.WriteLine("sleeping");
Thread.Sleep(1000);
Console.WriteLine("aborting");
thread.Abort();
Console.WriteLine("aborted");
thread.Join();
Console.WriteLine("joined");
Console.WriteLine("didfinally=" + didfinally);
Console.Read();
}
}

你认为这个程序的输出会是什么?

  1. didfinally=真
  2. didfinally=假

在您猜测之前阅读文档。我在下面包括了相关部分。

A constrained execution region (CER) is part of a mechanism for authoring reliable managed code. A CER defines an area in which the common language runtime (CLR) is constrained from throwing out-of-band exceptions that would prevent the code in the area from executing in its entirety. Within that region, user code is constrained from executing code that would result in the throwing of out-of-band exceptions. The PrepareConstrainedRegions method must immediately precede a try block and marks catch, finally, and fault blocks as constrained execution regions. Once marked as a constrained region, code must only call other code with strong reliability contracts, and code should not allocate or make virtual calls to unprepared or unreliable methods unless the code is prepared to handle failures. The CLR delays thread aborts for code that is executing in a CER.

The reliability try/catch/finally is an exception handling mechanism with the same level of predictability guarantees as the unmanaged version. The catch/finally block is the CER. Methods in the block require advance preparation and must be noninterruptible.

我现在特别关心的是防止线程中止。有两种类型:通过 Thread.Abort 的正常类型,以及 CLR 主机可以对您进行所有中世纪操作并强制中止的类型。 finally block 已经在某种程度上防止了 Thread.Abort。然后,如果您将 finally block 声明为 CER,那么您也会获得针对 CLR 主机中止的额外保护……至少我认为这是理论。

所以根据我所知道的,我猜到了#1。它应该打印 didfinally=True。 ThreadAbortException 被注入(inject),而代码仍在 try block 中,然后 CLR 允许 finally block 按预期运行,即使没有CER 对吗?

嗯,这不是我得到的结果。我得到了一个完全出乎意料的结果。 #1 或#2 都没有发生在我身上。相反,我的程序卡在 Thread.Abort。这是我观察到的。

  • PrepareConstrainedRegions 的存在延迟了 try block 内的线程中止。
  • PrepareConstrainedRegions 的缺失允许它们在 try block 中。

所以百万美元的问题是为什么?文档在我能看到的任何地方都没有提到这种行为。事实上,我正在阅读的大部分内容实际上是在建议您将关键的不可中断代码放在 finally block 中,专门用于防止线程中止。

也许,PrepareConstrainedRegions 除了 finally block 之外,还延迟了 try block 中的正常中止。但是 CLR 主机中止仅在 CER 的 finally block 中延迟?谁能更清楚地说明这一点?

最佳答案

[接评论]

我会将我的回答分为两部分:CER 和处理 ThreadAbortException。

我不认为 CER 最初旨在帮助解决线程中止问题;这些不是你要找的机器人。有可能我也误解了问题的陈述,这些东西往往会变得很沉重,但我发现文档中的关键短语(诚然,其中一个实际上与我提到的不同)是:

代码不能导致带外异常

用户代码使用可靠的 try/catch/finally 创建不可中断区域,*包含一个空的 try/catch block *,前面是 PrepareConstrainedRegions 方法调用

尽管没有直接在受约束的代码中受到启发,但线程中止是一种带外异常。受限区域仅保证,一旦 finally 执行,只要它遵守其 promise 的约束,它就不会因托管运行时操作而中断,否则不会中断非托管 finally block 。线程中止会中断非托管代码,就像它们会中断托管代码一样,但如果没有受限区域, 会有一些保证,并且可能还有针对您正在寻找的行为的不同推荐模式。我怀疑这主要是作为防止垃圾收集线程挂起的障碍(如果我不得不猜测的话,可能是通过在该区域的持续时间内将线程从抢占式垃圾收集模式切换出来)。我可以想象将它与弱引用、等待句柄和其他低级别管理例程结合使用。

至于意外行为,我的想法是你没有满足你通过声明约束区域 promise 的契约(Contract),所以结果没有记录,应该被认为是不可预测的。 Thread Abort 会在 try 中延迟,这看起来确实很奇怪,但我认为这是无意使用的副作用,这只值得进一步探索运行时的学术理解(一类知识是易变的,因为不能保证 future 的更新可能会改变这种行为)。

现在,我不确定以非预期方式使用上述副作用的程度有多大,但如果我们退出使用力来影响我们的控制 body 并让事情按照它们的方式运行通常情况下,我们确实会得到一些保证:

  • 在某些情况下,Thread.ResetAbort 可以防止线程中止
  • ThreadAbortExceptions 可以被捕获;整个 catch block 将运行,并且如果中止未重置,ThreadAbortException 将在退出 catch block 时自动重新抛出。
  • 保证在 ThreadAbortException 展开调用堆栈时运行所有 finally block 。

有了这个,这里有一个技术示例意味着在需要中止弹性的情况下使用。我在一个示例中混合了多种技术,这些技术没有必要同时使用(通常您不会)只是为了根据您的需要提供一些选项示例。

bool shouldRun = true;
object someDataForAnalysis = null;

try {

while (shouldRun) {
begin:
int step = 0;
try {

Interlocked.Increment(ref step);
step1:
someDataForAnalysis = null;
Console.WriteLine("test");

Interlocked.Increment(ref step);
step2:

// this does not *guarantee* that a ThreadAbortException will not be thrown,
// but it at least provides a hint to the host, which may defer abortion or
// terminate the AppDomain instead of just the thread (or whatever else it wants)
Thread.BeginCriticalRegion();
try {

// allocate unmanaged memory
// call unmanaged function on memory
// collect results
someDataForAnalysis = new object();
} finally {
// deallocate unmanaged memory
Thread.EndCriticalRegion();
}

Interlocked.Increment(ref step);
step3:
// perform analysis
Console.WriteLine(someDataForAnalysis.ToString());
} catch (ThreadAbortException) {
// not as easy to do correctly; a little bit messy; use of the cursed GOTO (AAAHHHHHHH!!!! ;p)
Thread.ResetAbort();

// this is optional, but generally you should prefer to exit the thread cleanly after finishing
// the work that was essential to avoid interuption. The code trying to abort this thread may be
// trying to join it, awaiting its completion, which will block forever if this thread doesn't exit
shouldRun = false;

switch (step) {
case 1:
goto step1;
break;
case 2:
goto step2;
break;
case 3:
goto step3;
break;
default:
goto begin;
break;
}
}
}

} catch (ThreadAbortException ex) {
// preferable approach when operations are repeatable, although to some extent, if the
// operations aren't volatile, you should not forcibly continue indefinite execution
// on a thread requested to be aborted; generally this approach should only be used for
// necessarily atomic operations.
Thread.ResetAbort();
goto begin;
}

我不是 CER 方面的专家,所以如果我有任何误解,请告诉我。我希望这会有所帮助:)

关于c# - 可以解释 PrepareConstrainedRegions 和 Thread.Abort 的这种意外行为吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18501678/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com