三、栈内存

1、每个WINDOWS都有一个栈基址和栈限址,二者合在一起表示栈的有效内存范围。栈限址不是固定的,程序需要更多内存空间里,栈限址没有超过保留的栈内存范围,则可以要求更多的内存页

2、在栈限址外是栈的守护页,当访问到守护页时,会引发STATUS_GUARD_PAGE_VIOLATION异常,当异常发生后,操作系统捕捉后,提交下一页内存,然后将其做为新的守护页,这相当于分配了一个新的页给程序访问,就是栈动态增长。
具体机制是当访问了带PAGE_GUARD 属性的内存时,操作系统意识到当前栈内存已经用完,因为栈内存的下一页就是守护页,操作系统马上清除守护页的PAGE_GUARD 标志,将其用于补充当前的栈内存,同时申请一个新的内存页做为守护页,随着新守护页的不断申请,越来越多的老守护页加入当前栈空间,这就是动态增长

3、从MSDN中可以查到
A guard page provides a one-shot alarm for memory page access. This can be useful for an application that needs to monitor the growth of large dynamic data structures. For example, there are operating systems that use guard pages to implement automatic stack checking.

To create a guard page, set the PAGE_GUARD page protection modifier for the page. This value can be specified, along with other page protection modifiers, in the VirtualAlloc, VirtualAllocEx, VirtualProtect, and VirtualProtectEx functions. The PAGE_GUARD modifier can be used with any other page protection modifiers, except PAGE_NOACCESS.

If a program attempts to access an address within a guard page, the system raises a STATUS_GUARD_PAGE_VIOLATION (0x80000001) exception. The system also clears the PAGE_GUARD modifier, removing the memory page’s guard page status. The system will not stop the next attempt to access the memory page with a STATUS_GUARD_PAGE_VIOLATION exception.

If a guard page exception occurs during a system service, the service fails and typically returns some failure status indicator. Since the system also removes the relevant memory page’s guard page status, the next invocation of the same system service won’t fail due to a STATUS_GUARD_PAGE_VIOLATION exception (unless, of course, someone reestablishes the guard page).

The following short program illustrates the behavior of guard page protection.

保护页提供了一个内存页的访问警告,对应用程序监视,操作系统用它来实现栈的自动检查
在 VirtualAlloc, VirtualAllocEx, VirtualProtect, and VirtualProtectEx 函数中可以定义这个参数

目前我们使用VB.NET进行多线程编程,使用的是.NET的CLR托管线程,CLR使用了SetThreadStackGuarantee来以更大的增量来递增栈限址,这个API指定了守护区域大小,如果将大小设置为0,则返回当前的守护区域大小。

3、运用stacktrace类实现线程栈回溯跟踪

代码如下:

Imports System
Imports System.Threading
Imports System.Diagnostics.StackTraceModule Module1Sub Main()Dim main_x As Integermain_x = 5Call sub1(main_x)End SubPrivate Sub sub1(sub1_x As Integer)Dim jg As Integerjg = sub1_x * sub1_xCall sub2(jg)End SubPrivate Sub sub2(sub2_x As Integer)Dim jg As Integerjg = sub2_x * 2jg = jg * jgDim st As New StackTrace(True)Console.WriteLine(" 栈跟踪: {0}", _st.ToString())Dim i As IntegerFor i = 0 To st.FrameCount - 1Dim sf As StackFrame = st.GetFrame(i)Console.WriteLine()Console.WriteLine("回溯调用栈, 方法: {0}", _sf.GetMethod())Console.WriteLine("回溯调用栈, 行号 : {0}", _sf.GetFileLineNumber())Next iEnd SubEnd Module

下面这句定义了StackTrace 类的新实例,实现线程栈跟踪

StackTrace 是使用调用方的当前线程创建的,参数含义如下:

如果为 true,则捕获文件名、行号和列号;否则为 false。

Dim st As New StackTrace(True)

3、运用stacktrace类实现线程栈回溯跟踪

代码如下:

Imports System
Imports System.Threading
Imports System.Diagnostics.StackTraceModule Module1Sub Main()Dim main_x As Integermain_x = 5Call sub1(main_x)End SubPrivate Sub sub1(sub1_x As Integer)Dim jg As Integerjg = sub1_x * sub1_xCall sub2(jg)End SubPrivate Sub sub2(sub2_x As Integer)Dim jg As Integerjg = sub2_x * 2jg = jg * jgDim st As New StackTrace(True)Console.WriteLine(" 栈跟踪: {0}", _st.ToString())Dim i As IntegerFor i = 0 To st.FrameCount - 1Dim sf As StackFrame = st.GetFrame(i)Console.WriteLine()Console.WriteLine("回溯调用栈, 方法: {0}", _sf.GetMethod())Console.WriteLine("回溯调用栈, 行号 : {0}", _sf.GetFileLineNumber())Next iEnd SubEnd Module

下面这句定义了StackTrace 类的新实例,实现线程栈跟踪

StackTrace 是使用调用方的当前线程创建的,参数含义如下:

如果为 true,则捕获文件名、行号和列号;否则为 false。

Dim st As New StackTrace(True)

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 MyStructPublic m_outputHandle As IntPtr
End Structure 'MyStructNotInheritable Class MySafeHandleInherits SafeHandle' Called by P/Invoke when returning SafeHandlesPublic Sub New()MyBase.New(IntPtr.Zero, True)End SubPublic Function AllocateHandle() As MySafeHandle' Allocate SafeHandle first to avoid failure later.Dim sh As New MySafeHandle()RuntimeHelpers.PrepareConstrainedRegions()TryFinallyDim myStruct As New MyStruct()NativeAllocateHandle(myStruct)sh.SetHandle(myStruct.m_outputHandle)End TryReturn shEnd Function

2)ProbeForSufficientStack

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

使用方式是

RuntimeHelpers.ProbeForSufficientStack()

分配或使用栈内存的语句

3)此外

RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup 也很重要

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

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

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

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

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

vb.net2019-多线程并行计算(4)相关推荐

  1. python doc2 —— MPI多线程并行计算工具mpi4py

    MPI多线程并行计算工具mpi4py 1. MPI 2. 基本MPI函数 2.1 工具 a. 通信子(通信空间) b. 获取进程 3. 通信 3.1 点对点通信 3.2 群体通信 a. 广播 bcas ...

  2. vb.net2019-多线程并行计算(2)

    一.TLS种类 1)动态TLS 2)静态TLS 静态TLS的速度比动态TLS快,在编译期就决定,需要定义一个静态域来表示TLS数据,编译器有足够的信息来在编译期间内发射代码,动态TLS需要通过一个或多 ...

  3. VB无所不能之七:VB的多线程(2)

    云查杀很牛X的金山毒霸!免费啦!2011下载链接! 久违啦,我今天终于又可以写博客了,VB无所不能这个系列写作途中总是磕磕绊绊的,终于写到第七篇多线程的时候,中断了长达1个多月的时间没有更新新帖,对大 ...

  4. vb.net2019-多线程并行计算(1)

    Imports System Imports System.ThreadingModule Module1Sub Main()Dim mythread1 As ThreadDim mythread2 ...

  5. linux 多线程并行计算,Linux下使用POSIX Thread作多核多线程并行计算

    POSIX线程库根据处理器.操作系统等特性封装了一台线程处理的接口.对于目前基于x86处理器架构的Linux系统来说,它往往会默认地将新创建的一个线程调度到与主线程不同的核中执行,如果这样能更好地平衡 ...

  6. vb.net2019-多线程并行计算(6)

    Imports System Imports System.Threading Imports System.Diagnostics Imports System.Diagnostics.Thread ...

  7. vb.net2019-多线程并行计算(5)

    一.线程状态涉及的CLR类 1)Process 类 提供对本地和远程进程的访问并使您能够启动和停止本地系统进程 GetCurrentProcess 获取新的 Process 组件并将其与当前活动的进程 ...

  8. vb.net2019-多线程并行计算(3)

    接上节,我们可以使用下面语句创建一个线程本地变量,利用静态TLS功能 Dim betterCounter As ThreadLocal(Of Integer) = New ThreadLocal(Of ...

  9. linux 多线程并行计算,浅谈.NET下的多线程和并行计算(五)线程池基础上

    池(Pool)是一个很常见的提高性能的方式.比如线程池连接池等,之所以有这些池是因为线程和数据库连接的创建和关闭是一种比较昂贵的行为.对于这种昂贵的资源我们往往会考虑在一个池容器中放置一些资源,在用的 ...

最新文章

  1. php获取请求路径_如何获取php.ini文件路径?
  2. Centos6.5集群安装64位hadoop2.2.0
  3. SimBERTv2来了!融合检索和生成的RoFormer-Sim模型
  4. [安卓] 7、页面跳转和Intent简单用法
  5. mysql内部实现原理面试_理解完这些基本上能解决面试中MySql的事务问题
  6. eureka自我保护功能
  7. 信息学奥赛一本通(1135:配对碱基链)
  8. 使用 Nginx + Gunicorn 部署 Flask 项目
  9. ×××技术漫谈之IPSec(附MPLS)
  10. web安全day40:最新版OpenVAS的安装配置
  11. linux光盘挂载加载过程,如何在Linux系统下挂载光盘
  12. 8个适合新手的Python小项目
  13. 数学分析教程(科大)——3.3笔记+习题
  14. CDUTCM OJ 2017第一次月赛
  15. 大连python薪酬_大连Python开发靠谱吗
  16. Android学习之路——转自stormzhang
  17. R语言survival包clogit函数构建条件logistic回归模型、summary函数查看模型汇总统计信息、通过似然比检验分析结果判断模型有无统计学意义
  18. lnux php 连接access,在Linux下实现对Microsoft Access Database(.mdb
  19. mac-lol.tk syjc.html,苹果 MacBook Air笔记本一键u盘装系统win7教程
  20. 【飞桨PaddlePaddle学习心得】被遗忘的8张图片(day2爬虫的王姝慧的问题)

热门文章

  1. 解决hao123胁持chrome等浏览器主页问题
  2. 用反射通过构造函数给窗体传参
  3. lua--面向对象使用middleclass
  4. 移动端H5页面注意事项
  5. ActiveMQ的简单例子应用
  6. 如何在eclipse中修改jsp默认编码
  7. createQuery与createSQLQuery
  8. input高级限制级用法
  9. 云服务器开启ftp_FTP是什么?FTP和虚拟主机的关系
  10. 肿瘤动物模型中需要注意的伦理问题