VB.NET并行与分布式编程(四)-线程栈[4]

VB.NET并行与分布式编程(4)-线程栈[4]

 4、栈溢出

1)溢出情况

a)线程试图提交比保留大小更多的栈内存页

b)没有物理内存也没有虚拟内存可供提交更多的守护页

2、栈溢出后还想继续运行程序,必须重置守护页,可以使用CRT的_resetstkoflw。


不过对于大的数据,可以考虑将某些数据移到堆中。堆栈是有限的,甚至在用户模式下也是如此,如果无法提交堆栈页,会导致堆栈溢出异常。_resetstkoflw 函数可以将系统从堆栈溢出的情况恢复为正常,从而使程序得以继续运行,而不会由于出现异常错误而失败。如果未调用 _resetstkoflw 函数,则在上一个异常后不会显示保护页。当下次发生堆栈溢出时,根本不会显示异常,进程将在没有任何警告的情况下终止。


System.Runtime.CompilerServices.RuntimeHelpers在线程栈空间的探查方面有很大的作用

下面这是这个类的常用方法


EnsureSufficientExecutionStack  确保剩余的堆栈空间足够大,可以执行一般的 .NET Framework 函数。 
   Equals(Object, Object)  确定指定的 Object 实例是否被视为相等。 
   ExecuteCodeWithGuaranteedCleanup  使用一个 Delegate 执行代码,同时使用另一个 Delegate 在异常情况下执行附加代码。 
   GetHashCode(Object)  用作特定类型的哈希函数,适合在哈希算法和数据结构(如哈希表)中使用。 
     GetObjectValue  将值类型装箱。 
     InitializeArray  提供从存储在模块中的数据初始化数组的快速方法。 
   PrepareConstrainedRegions  将代码体指定为受约束的执行区域 (CER)。 
   PrepareConstrainedRegionsNoOP  指定代码体为受约束的执行区域 (CER),而不执行任何探测。 
   PrepareContractedDelegate  提供应用程序用来动态准备 AppDomain 事件委托的方法。 
   PrepareDelegate  指示应准备指定委托以包含在受约束的执行区域 (CER) 中。 
   PrepareMethod(RuntimeMethodHandle)  准备一个要包含在受约束的执行区域 (CER) 中的方法。 
   PrepareMethod(RuntimeMethodHandle, RuntimeTypeHandle())  准备一个要包含在受约束的执行区域 (CER) 中的具有指定实例化的方法。 
   ProbeForSufficientStack  探测某个数量的堆栈空间,以确保不会在后续的代码块内发生堆栈溢出(假设您的代码仅使用有限适中的堆栈空间)。 建议使用受约束的执行区域 (CER),而不使用此方法。 
   RunClassConstructor  运行指定的类构造函数方法。 
   RunModuleConstructor  运行指定的模块构造函数方法。 

 

 

其中比较常用的是

ProbeForSufficientStack  和PrepareConstrainedRegions

 从MSDN中可看出以下细节:

1)PrepareConstrainedRegions
编译器使用此方法来将 catch、finally 和 fault 块标记为受约束的执行区域 (CER)。 标记为受约束的区域的代码必须只调用其他具有高可靠性协定的代码。 它不能分配或虚调用未准备的或不可靠的方法,除非它已准备好处理失败。

请注意,除了 NOP 之外,在对 PrepareConstrainedRegions 方法的调用和 try 块之间不允许使用其他任何中间语言操作码。

受约束的执行区域 (CER) 是创作可靠托管代码的机制的一部分。CER 定义一个区域,在该区域中公共语言运行库 (CLR) 会受到约束,不能引发可使区域中的代码无法完全执行的带外异常。在该区域中,用户代码受到约束,不能执行会导致引发带外异常的代码。PrepareConstrainedRegions 方法必须直接位于 try 块之前,并将 catch、finally 和 fault 块标记为受约束的执行区域。标记为受约束的区域后,代码只能调用其他具有强可靠性约定的代码,而且代码不应分配或者对未准备好的或不可靠的方法进行虚调用,除非代码已经准备好处理错误。CLR 为 CER 中正在执行的代码延迟线程中止。

除批注的 try 块外,受约束的执行区域还以其他形式用于 CLR 中


 

CLR 会事先准备 CER 以避免出现内存不足的情况。进行事先准备的目的是为了避免 CLR 在实时编译或类型加载时发生内存不足的情况。

CER 中不允许下面的操作:

显式分配。

获取锁。

装箱。

多维数组访问。

通过反射进行的方法调用。

Enter 或 Lock。

安全检查。不执行命令,仅链接命令。

COM 对象和代理的 Isinst 和 Castclass

获取或设置透明代理上的字段。

序列化。

函数指针和委托。

 

 如果您计划使用适当数量的堆栈空间,则使用 try/finally 或 try/catch 块后跟对 RuntimeHelpers.PrepareConstrainedRegions 方法的调用。 如果您正在调用递归方法或计划使用大量堆栈空间,则必须使用 RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup 方法。

可如下构造代码

      

       RuntimeHelpers.PrepareConstrainedRegions()
        Try
            分配大量堆栈空间的代码
        Finally
             分配失败后的清理工作或安全分配空间的代码

        End Try

 

下面是msdn中可靠地设置句柄的操作:

要可靠地将句柄设置为指定的预先存在的句柄,必须确保本机句柄的分配,以及该句柄在SafeHandle 对象中的后续记录是原子操作这些操作之间,如果出现任何故障(如线程中止或内存不足异常)都会导致该本机句柄泄漏。 可以使用 PrepareConstrainedRegions 方法确保句柄不会泄漏。

 

 

<StructLayout(LayoutKind.Sequential)> _
Structure MyStruct
    Public m_outputHandle As IntPtr
End Structure 'MyStruct


NotInheritable Class MySafeHandle
    Inherits SafeHandle

    ' Called by P/Invoke when returning SafeHandles
    Public Sub New()
        MyBase.New(IntPtr.Zero, True)

    End Sub


    Public Function AllocateHandle() As MySafeHandle
        ' Allocate SafeHandle first to avoid failure later.
        Dim sh As New MySafeHandle()

        RuntimeHelpers.PrepareConstrainedRegions()
        Try
        Finally
            Dim myStruct As New MyStruct()
            NativeAllocateHandle(myStruct)
            sh.SetHandle(myStruct.m_outputHandle)
        End Try

        Return sh

    End Function

 2)ProbeForSufficientStack

探测某个数量的堆栈空间,以确保不会在后续的代码块内发生堆栈溢出(假设您的代码仅使用有限适中(12页栈内存)的堆栈空间)。 建议使用受约束的执行区域 (CER),而不使用此方法。

使用方式是

RuntimeHelpers.ProbeForSufficientStack()

分配或使用栈内存的语句

3)此外

RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup 也很重要

Public Shared Sub ExecuteCodeWithGuaranteedCleanup ( _
code As RuntimeHelpers..::..TryCode, _ backoutCode As RuntimeHelpers..::..CleanupCode, _ userData As Object _
)

从其参数就可以看出它对处理栈溢出的重要性了

 

 

code
类型:System.Runtime.CompilerServices.RuntimeHelpers.TryCode
要尝试的代码的委托。

backoutCode
类型:System.Runtime.CompilerServices.RuntimeHelpers.CleanupCode
在发生异常时要运行的代码的委托。

userData
类型:System.Object
要传递给 code 和 backoutCode 的数据