接上节,我们可以使用下面语句创建一个线程本地变量,利用静态TLS功能

Dim betterCounter As ThreadLocal(Of Integer) = New ThreadLocal(Of Integer)(Function() 1)

betterCounter的值初始化为1。在本程序中,jg被初始化为50,并定义成线程本地变量

    Dim jg As ThreadLocal(Of Integer) = New ThreadLocal(Of Integer)(Function() 50)

然后,我们使用jg.Value 来读写这个本地变量的值

jg.Value -= mynum

三、动态TLS

Imports System
Imports System.Threading

Module Module1

Sub Main()Dim mythread1 As ThreadDim mythread2 As ThreadDim mythread3 As Thread'创建线程对象mythread1 = New Thread(AddressOf mythreadrun)mythread2 = New Thread(AddressOf mythreadrun)mythread3 = New Thread(AddressOf mythreadrun)Console.WriteLine(Now.ToLongTimeString & "线程对象创建完毕,开始执行线程")'执行线程mythread1.Start("线程1")mythread2.Start("线程2")mythread3.Start("线程3")'等待线程完成mythread1.Join()mythread2.Join()mythread3.Join()'线程执行完毕Console.WriteLine(Now.ToLongTimeString & "线程执行完毕!")
End Sub
Public Sub mythreadrun(ByVal data As Object)Dim mynum As Integer'分配一个新的槽,这个槽存放线程本地数据,槽名称'必须唯一Thread.AllocateNamedDataSlot(data)Dim jg As LocalDataStoreSlotjg = Thread.GetNamedDataSlot(data)Thread.SetData(jg, 100)TryFor mynum = 1 To 10Thread.SetData(jg, Thread.GetData(jg) - mynum)Console.WriteLine(data & "  " & Now.ToLongTimeString & "=>" & (Thread.GetData(jg) + mynum) & "-" & mynum & ",计算结果为:" & Thread.GetData(jg))Thread.Sleep(2)NextCatchConsole.WriteLine(data & "  " & Now.ToLongTimeString & "线程异常终止!")'终止线程Thread.CurrentThread.Abort()FinallyThread.FreeNamedDataSlot(data)End Try
End Sub

End Module

运行结果如我们所愿,jg变量通过动态TLS提供的槽机制实现了线程本地变量

[点击并拖拽以移动]

    '分配一个新的槽,这个槽存放线程本地数据,槽名称为'myjg,名称必须唯一Thread.AllocateNamedDataSlot(data)Dim jg As LocalDataStoreSlotjg = Thread.GetNamedDataSlot(data)Thread.SetData(jg, 100)

以上代码是关健,我们使用命名数据槽,当然,我们也可以使用未命名槽,因为未命名数据槽相对较简单,所以这里使用了命名数据槽,向大家演示一下其功能。

注意:

如果使用 AllocateNamedDataSlot 方法已分配已经存在的指定名称的槽,此方法会引发异常,且无法测试是否已分配某个槽。另外,使用此方法分配的数据槽必须使用 FreeNamedDataSlot 来释放。

本例中,我们分配槽使用下面语句

Thread.AllocateNamedDataSlot(data)

获取某个命名槽的引用,以便进行下一步操作

jg = Thread.GetNamedDataSlot(data)

Thread.SetData和Thread.GetData可写、读槽中数据

四、数据槽的值在线程或上下文对象之间不共享

LocalDataStoreSlot 结构可用作本地存储内存机制,线程和上下文可以使用此机制分别存储线程特定的数据和上下文特定的数据。 公共语言运行时在创建每个进程时给它分配一个多槽数据存储区数组。 线程或上下文调用各种函数在数据存储区中分配数据槽、在槽内存储和检索数据值、以及释放数据槽以便在线程或上下文过期后重新使用它。

对于每个线程或上下文,数据槽都是唯一的;它们的值在线程或上下文对象之间不共享。 数据槽可根据名称或根据索引号来分配。

我们可以从下面程序看出

Imports System
Imports System.ThreadingModule Module1Sub Main()Dim mythread1 As ThreadDim mythread2 As ThreadDim mythread3 As ThreadDim jg As LocalDataStoreSlot'创建线程对象mythread1 = New Thread(AddressOf mythreadrun)mythread2 = New Thread(AddressOf mythreadrun)mythread3 = New Thread(AddressOf mythreadrun)Console.WriteLine(Now.ToLongTimeString & "线程对象创建完毕,开始执行线程")'jg = Thread.AllocateNamedDataSlot("myjg")' Thread.SetData(jg, 100)'执行线程mythread1.Start("线程1")mythread2.Start("线程2")mythread3.Start("线程3")'等待线程完成mythread1.Join()mythread2.Join()mythread3.Join()'线程执行完毕Console.WriteLine(Now.ToLongTimeString & "线程执行完毕!")Thread.FreeNamedDataSlot("myjg")End SubPublic Sub mythreadrun(ByVal data As Object)Dim randomGenerator As New Random()Dim mynum As Integer'分配一个新的槽,这个槽存放线程本地数据,槽名称为   'myjg   Dim jg As LocalDataStoreSlotTryjg = Thread.AllocateNamedDataSlot("myjg")Catchjg = Thread.GetNamedDataSlot("myjg")End TryThread.SetData(jg, 100)TryFor mynum = 1 To 10Thread.SetData(jg, Thread.GetData(jg) - mynum)Console.WriteLine(data & "  " & Now.ToLongTimeString & "=>" & (Thread.GetData(jg) + mynum) & "-" & mynum & ",计算结果为:" & Thread.GetData(jg))Thread.Sleep(randomGenerator.Next(10, 200))NextCatchConsole.WriteLine(data & "  " & Now.ToLongTimeString & "线程异常终止!")'终止线程Thread.CurrentThread.Abort()End TryEnd Sub
End Module

为了查看效果,我特意用随机数来代替固定的sleep时间,这样更有说明力。

五、TLS小结

1)TLS基础

可以使用托管线程本地存储区 (TLS) 存储某一线程和应用程序域所独有的数据。 .NET Framework 提供了两种使用托管 TLS 的方式:线程相关的静态字段和数据槽。 线程相关的静态字段提供的性能比数据槽的性能要好得多,而且它还启用了编译时类型检查。

如果您可以在编译时预料到您的确切需要,请使用线程相关的静态字段(在 Visual Basic 中为线程相关的 Shared 字段)。 线程相关的静态字段可提供最佳性能。 它们还具备编译时类型检查的优点。

如果只能在运行时发现您的实际需要,请使用数据槽。 数据槽比线程相关的静态字段慢一些且更加难于使用,并且数据存储为 Object 类型,因此必须将其强制转换为正确的类型才能使用。

2)2种TLS特点
a)无论是使用线程相关的静态字段还是使用数据槽,托管 TLS 中的数据都是线程和应用程序域组合所独有的。
在应用程序域内部,一个线程不能修改另一个线程中的数据,即使这两个线程使用同一个字段或槽时也不能。
当线程从多个应用程序域中访问同一个字段或槽时,会在每个应用程序域中维护一个单独的值。
例如,如果某个线程设置线程相关的静态字段的值,接着它进入另一个应用程序域,然后检索该字段的值,则在第二个应用程序域中检索的值将不同于第一个应用程序域中的值。 在第二个应用程序域中为该字段设置一个新值不会影响第一个应用程序域中该字段的值。 同样,当某个线程获取两个不同应用程序域中的同一命名数据槽时,第一个应用程序域中的数据将始终与第二个应用程序域中的数据无关。
b)如果您知道一些数据总是某个线程和应用程序域组合所独有的,请向该静态字段应用 ThreadStaticAttribute 特性。 与使用任何其他静态字段一样使用该字段。 该字段中的数据是每个使用它的线程所独有的。线程相关的静态字段的性能优于数据槽,并且具有编译时类型检查的优点。
c)请注意,任何类构造函数代码都将在访问该字段的第一个上下文中的第一个线程上运行。 在同一应用程序域内的所有其他线程或上下文中,如果字段是引用类型,它们将被初始化为 null(在 Visual Basic 中为 Nothing);如果字段是值类型,它们将被初始化为它们的默认值。 因此,您不应依赖于类构造函数来初始化线程相关的静态字段。 而应避免初始化线程相关的静态字段并假定它们初始化为 null (Nothing) 或它们的默认值。
d)在 .NET Framework 4 版中,可以使用 System.Threading.ThreadLocal(Of T) 类创建线程本地对象,在第一次使用该对象时它将惰式初始化,这样就解决了c中所指问题。
d).NET Framework 提供了线程和应用程序域组合所独有的动态数据槽。 数据槽包括两种类型:命名槽和未命名槽。 两者都是通过使用 LocalDataStoreSlot 结构来实现的。
若要创建命名数据槽,请使用 Thread.AllocateNamedDataSlot 或 Thread.GetNamedDataSlot 方法。 若要获取对某个现有命名槽的引用,请将其名称传递给 GetNamedDataSlot 方法。
若要创建未命名数据槽,请使用 Thread.AllocateDataSlot 方法。
e)对于命名槽和未命名槽,请使用 Thread.SetData 和 Thread.GetData 方法设置和检索槽中的信息。 这些都是静态方法,它们始终作用于当前正在执行它们的线程的数据。
f)命名槽可能很方便,因为您可以在需要它时通过将其名称传递给 GetNamedDataSlot 方法来检索该槽,而不是维护对未命名槽的引用。 但是,如果另一个组件使用相同的名称来命名其线程相关的存储区,并且有一个线程同时执行来自您的组件和该组件的代码,则这两个组件可能会破坏彼此的数据。 (本方案假定这两个组件在同一应用程序域内运行,并且它们并不用于共享相同数据。)
g)线程使用本地存储内存机制来存储线程特定的数据。 公共语言运行时在创建每个进程时给它分配一个多槽数据存储区数组。 线程可以分配数据存储区中的数据槽,存储和检索槽中的数据值,以及在线程到期之后释放槽以供重新使用。 每个线程的数据槽都是唯一的。 其他任何线程(即便是子线程)均无法获取该数据。

1、用调试器调试线程

1)栈调用

以下面代码为例

Imports System.ThreadingPublic Class Form1Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.ClickDim 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 * 2'在下一句设置断点jg = jg * jgEnd SubEnd Class

此外,我们还可以研究一下这个线程的调用时堆栈情况,通过反汇编代码,在调用堆栈窗口中选择“转到反汇编”

   Private Sub sub2(sub2_x As Integer)
00000000  push        ebp
00000001  mov         ebp,esp
00000003  sub         esp,14h
00000006  mov         dword ptr [ebp-10h],ecx
00000009  mov         dword ptr [ebp-4],edx
0000000c  cmp         dword ptr ds:[0256B1B8h],0
00000013  je          0000001A
00000015  call        62A16743
0000001a  xor         edx,edx
0000001c  mov         dword ptr [ebp-8],edx
0000001f  mov         eax,dword ptr [ebp-10h]
00000022  mov         dword ptr [ebp-14h],eax
00000025  mov         ecx,dword ptr [ebp-10h]
00000028  call        628F5C25
0000002d  mov         dword ptr [ebp-0Ch],eax
00000030  push        32h
00000032  mov         edx,dword ptr [ebp-0Ch]
00000035  mov         ecx,dword ptr [ebp-14h]
00000038  call        FFDF30D0
0000003d  mov         ecx,dword ptr [ebp-4]
00000040  call        FFFFFF70
00000045  mov         ecx,63h
0000004a  call        FFDF2940
0000004f  nop Dim jg As Integerjg = sub2_x * 2
00000050  mov         eax,dword ptr [ebp-4]
00000053  mov         edx,2
00000058  imul        eax,eax,2
0000005b  jno         00000062
0000005d  call        62A19A30
00000062  mov         dword ptr [ebp-8],eax '在下一句设置断点jg = jg * jg
00000065  mov         eax,dword ptr [ebp-8]
00000068  imul        eax,dword ptr [ebp-8]
0000006c  jno         00000073
0000006e  call        62A19A30
00000073  mov         dword ptr [ebp-8],eax End Sub
00000076  nop
00000077  nop
00000078  mov         ecx,63h
0000007d  call        FFDF2A60
00000082  nop
00000083  mov         esp,ebp
00000085  pop         ebp
00000086  ret ESP是栈顶指针ebp是基址指针+-----++基址 ++-----++栈内容++栈内容++栈内容++栈内容++栈内容+

+栈顶 +

如上图所示,基地的地址比栈顶的地址大,就是向下增长。

寄存器ebp和esp保存着当前的基址和栈顶地址

首先,进入函数时

00000000 push ebp
备份基址指针

00000001 mov ebp,esp

然后设置基址指针指向栈顶,相当于为当前栈清空了内容,做好在栈中分配局部变量的准备
00000003 sub esp,14h

完成栈(可以理解为本函数可访问的栈)的空间分配,将栈顶指针向下增长14h(向下增长的意思是栈的空间增长规律是地址递减)。相当于栈中已经容纳了14h的空间

        Dim jg As Integerjg = sub2_x * 2
00000050  mov         eax,dword ptr [ebp-4]
00000053  mov         edx,2
00000058  imul        eax,eax,2
0000005b  jno         00000062
0000005d  call        617391D0
00000062  mov         dword ptr [ebp-8],eax '在下一句设置断点jg = jg * jg
00000065  mov         eax,dword ptr [ebp-8]
00000068  imul        eax,dword ptr [ebp-8]
0000006c  jno         00000073
0000006e  call        617391D0
00000073  mov         dword ptr [ebp-8],eax

从上面这段代码可以看出来

sub2_x分配在了dword ptr [ebp-4] ,而jg分配在了dword ptr [ebp-8]

最后,离开函数时,恢复进入函数前栈的指针,相当于释放了本次在栈中分配的空间

    End Sub
00000083  mov         esp,ebp
恢复栈顶指针00000085  pop         ebp恢复基址指针00000086  ret

2、修改默认栈的大小

 Dim 线程变量名 As Thread = New Thread(函数名,以字节为单位的栈大小)

比如

Dim mythread As Thread = New Thread(myfun,1024*512)

分配了512kb字节

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

  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-多线程并行计算(4)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

最新文章

  1. Scala 深入浅出实战经典 第96讲:Akka第一个案例动手实战main方法实现中ActorSystem等代码详解...
  2. 用户信号量及其PV操作处理实际问题
  3. linux网络编程之socket(十一):套接字I/O超时设置方法和用select实现超时
  4. JSON数据和Java对象的相互转换
  5. 【题意+解析】1041 Be Unique (20 分)_18行代码AC
  6. c语言课程设计作业摇一摇,口才星教案第二册.doc
  7. 使用Eclipse进行PHP的服务器端调试
  8. 推荐我们在B站的生信程序基础课
  9. 参加 CSDN 2009 英雄大会有感(二)
  10. python修改excel内容怎么覆盖_Python修改Excel的内容,python,excel
  11. javascript网页自动填表_javascript实现自动填写表单实例简析
  12. Matlab图像处理--低通滤波器的Matlab实现及透视图
  13. python进阶 pdf_Python进阶(Intermediate_Python)_中文PDF彩色版.pdf
  14. C#图形界面汉诺塔Hanoi
  15. 解决JS双击事件dblclick触发时,同时会执行click事件中的语句
  16. 查看用友NC的版本方法(不启动NC,只看NCHOME)
  17. Dynamics CRM 中 Xrm.Page.getControl('name').getValue() 和 Xrm.Page.getAttribute('name').getValue() 的区别
  18. 我的世界java百度什么电脑玩好_【我的世界】为了在龙芯电脑上玩Minecraft(我的世界)我做了什么_玩得好游戏攻略...
  19. 诺基亚发布NetAct云网络管理系统,为5G网络演进铺路
  20. sql语句两个练习 emp dept 复杂查询

热门文章

  1. yii2 basic版 MVC 部分
  2. [转]清除mysql表中数据
  3. 彻底的卸载SQL Server2005
  4. 二叉搜索树的查询操作《算法导论》12.2
  5. Python练习-基于socket的FTPServer
  6. Java实战之04JavaWeb-02Request和Response
  7. 九度oj 1006 ZOJ问题 2010年浙江大学计算机及软件工程研究生机试真题
  8. hud 4455 Substrings 解题报告
  9. LLBL Gen Pro 设计器使用指南
  10. DirectShow学习