一、线程基础
1.单线程的进程
每个进程至少有一个连续的执行顺序或线程。最初的线程即是基本线程或是主线程,线程的实际执行顺序是由应用程序的函数和子程序中的代码来决定的。
2. 多线程的进程
UI线程与工作者线程
有用户界面的一般称为UI线程,没有界面的称之为工作者线程。UI线程因为有界面,所以系统会给它维护一个消息队列,工作者线程默认是没有消息队列的.比如UI中一个按钮触发一个很耗时间的复杂计算,这个计算过程就可以通过一个工作者线程来实现。
单元线程与自由线程
在单元线程模式中,每个进程都被授予了需要执行的全局数据自己的副本。而每个生成的线程都是在它本身的进程中产生的,所以那些线程就不能共享在进程的内存中的数据。
在自由线程模式中,多个线程可以同时调用相同的方法和组件。与单元线程不同,自由线程不会被限制在独立的内存空间。当应用程序必须进行大量相似而又独立的数学计算时,您可能需要使用自由线程。在这种情况下,您需要生成多个线程使用相同的代码示例来执行计算。
提醒:自由线程类似于排球中的自由人,共享整个队伍(类似进程)的防守,又可随时下场上场(类似新建线程终结线程)
无论如何,同一时刻处理器上只能执行一个线程。
3.中断与线程本地存储的工作原理
一个时间片内线程若没完成任务,只有等到下次轮到执行它。这时停下时会存储当前状态信息,这就是线程本地存储(Thread Local Storage,TLS)。线程的TLS包括了寄存器、堆栈指针、调度信息、内存中的地址空间和使用中的其他资源的有关信息。存储在TLS中的一个寄存器是程序计数器,程序计数器的功能是告诉线程接下来执行哪条指令。
中断
windows本身有一个主线程(即系统线程),该线程负责所有其他线程的调度。windows决定了一个线程必须执行多长时间并在当前线程的执行次序中放置一条指令。这个周期在系统与系统之间甚至是同一系统上的线程与线程之间也会有所不同,因为中断被明显地放置在指令设置中,所以它称为软件中断(硬件中断在被执行的具体指令的外部发生)。
一旦放置了中断,windows就允许线程执行。当线程发生中断时,当前线程(粉底)的状态将存储在TLS中并自动移动排队到队列末(最下粉底)。程序计数器保存是当前执行的指令的地址(12处)。
4.线程睡眠与时钟中断
某线程需等待提供资源才能继续运行(如下载中的文件),在资源未准备齐全时,每次轮询进只好让给其它线程,为了不浪费cpu来回切换这个“不做功却占位”线程,该线程自动会自己执行退出执行队列一段一时间,称线程睡眠。
睡眠时再次打包TSL(但并未再次排列到可运行队列末),而是放于另一个睡眠队列上。为了唤醒它,将使用时钟中断,当时间中断发生时,若此时与睡眠队列上某线程恢复运行的时间相匹配,该线程将被移到可运行队列,再次恢复以前的执行过程。
5.线程的终止
在执行另一个线程时可以作为请求显式地停止线程的执行。当以这种方式结束线程时,就称为终止。当汍行到线程执行次序的结尾时,线程也会停止。
当线程结束时,线程的TLS就会被重新分配。线程所使用的进程屮的数据仍然存在,除非进程也结束了这些数据才会被淸除。不能从线程本身内部终止它们。必须从另一个线程才可以调用线程终止。
6.线程的优先级
优先级高的线程将抢先优先级较低的线程,抢先意味着它先运行。所有最高优先级的线程完成之后,仅次于它们优先级的线程就会被安排执行,以此类推。
windows将线程的优先级区分为了0-31个级别,数字越大优先级越高。只有系统才能够设置0优先级,而且意味着线程是空闲的。windows系统的用户可以设置1-15的优先级。如果需要设置一个高于15的优先级,则只有系统管理员身份才可以完成这项工作。以16-31之间的优先级运行着的线程被认为是在实时运行。
管理优先级:windows提供了几种设置管理级和程序级任务优先级的方式,比如任务管理器。
二、VB.NET对线程的支持
(一) System.AppDomain 类
1.应用程序域
.NET Framework新添加了一个隔离的层,该层被称为应用程序域。
应用程序域非进程那样物理上独立,仅是进程内部逻辑上独立。单独进程以存在多个应用程序域,同一应用程序域内线程可以使用全局数据,同样,线程还可以穿过应用程序域来执行。这样减少了域内通信与域间通信的开销,同时还提供数据类型检查功能。
应用程序域的功能都封装在System.AppDomain类中。一个应用程序中载入命名空间时,命名空间都被载入到了一个AppDomain中。应用程序域与线程也有着直接的关系,它们可以保存一个或多个线程,就像是一个进程可以包含一个或多个线程一样。不同之处在于:应用程序域可在进程内部创建且没有新线程(图#2)。
如图,一台计算机内有多个进程(Y,Z),每个应用程序(进程)有一个或多个应用程序域(AppDomain),每个AppDomain可创建并执行多个线程。
在.NET中,因为安全的原因AppDomain类和Thread类不能被继承。
2.为应用程序域设置数据
例:使用AppDomain来设置数据、检索数据以及标识AppDomain在执行的线程。(注意在项目属性中设置为Main启动,下同)
imports System.AppDomainPublic Class MyAppDomain Dim Domain As AppDomain '应用程序域,它是一个应用程序在其中执行的独立环境。 此类不能被继承 Public Sub SetDomainData(ByVal name As String,ByVal value As String) '为指定的应用程序域属性分配指定值 Domain.SetData(name,CType(value,Object)) 'name 要创建或更改的用户定义应用程序域属性的名称(String) 'data 属性的值(Object) End Sub Public Function GetDomainData(ByVal name As String) As String '为指定名称获取存储在当前应用程序域中的值 Return Domain.GetData(name) 'name 预定义应用程序域属性的名称,或已定义的应用程序域属性的名称(String) End Function Public Shared Sub Main() Console.Writeline("检索当前应用程序域...") Dim Obj As New MyAppDomain() Dim Dataname As String,DataValue As String Obj.Domain = CurrentDomain '获取当前 Thread 的当前应用程序域 Dataname = "MyData" DataValue = "Some Data To be stored" Console.Writeline("设置应用程序域数据...") Obj.SetDomainData(Dataname,DataValue) Console.Writeline("获取应用程序域数据...") Console.Writeline("The Data found for key '" & Dataname & "' is '" & Obj.GetDomainData(Dataname) & "' running on thread ID: " & Threading.Thread.CurrentThread.ManagedThreadID.ToString) Console.Readline() End SubEnd Class
3.在指定的应用程序域中执行代码
例:在新近创建的AppDomain中创建线程时的有关行为。
imports System.AppDomainPublic Class CreateAppDomain Public Shared Sub Main() Dim DomainA As AppDomain,DomainB As AppDomain Dim stringA As String = "DomainA Value" Dim stringB As String = "DomainB Value" DomainA = CreateDomain("MyDomainA") '使用指定的名称新建应用程序域 DomainB = CreateDomain("MyDomainB") DomainA.SetData("DomainKey",stringA) DomainB.SetData("DomainKey",stringB) CommonCallBack() '1 'DoCallBack(callBackDelegate As CrossAppDomainDelegate) '在另一个应用程序域中执行代码,该应用程序域由指定的委托标识 'callBackDelegate 指定要调用的方法的委托 DomainA.DoCallBack(AddressOf CommonCallBack) '2 DomainB.DoCallBack(AddressOf CommonCallBack) '3 Console.Readline() End Sub Shared Sub CommonCallBack() Dim Domain As AppDomain = CurrentDomain '当前应用程序域 Console.Writeline("The Value '" & Domain.GetData("DomainKey") & "' was found in " & Domain.FrIEndlyname & "running on thread ID:" & Threading.Thread.CurrentThread.ManagedThreadID.ToString) End SubEnd Class说明:第一排应用程序域名的值为空,是因为当前应用程序域无DomainKey,name有系统预定义名,如Domain.GetData("APP_name")是应用程序的名称。
三排中得出的线程ID号都是9,说明AppDomain可以有零个或多个线程,更重要的是—个线程能够跨越不同的域来执行。
(二)线程管理
.NETFramework提供自由线程和逻辑应用程序域的功能,还提供处理器线程类(System.Threading.Thread)。
受托管的线程与 windows线程(http://kb.cnblogs.com/page/68545/) 必须要了解,执行.NET应用的线程实际上仍然是windows线程。但是,当某个线程被CLR所知时,我们将它称为受托管的线程。具体来说,由受托管的代码创建出来的线程就是受托管的线程。如果一个线程由非托管的代码所创建,那么它就是非托管的线程。不过,一旦该线程执行了受托管的代码它就变成了受托管的线程。 一个受托管的线程和非托管的线程的区别在于,CLR将创建一个System.Threading.Thread类的实例来代表并 *** 作前者。在内部实现中,CLR将一个包含了所有受托管线程的列表保存在一个叫做ThreadStore地方。 CLR确保每一个受托管的线程在任意时刻都在一个AppDomain中执行,但是这并不代表一个线程将永远处在一个AppDomain中,它可以随着时间的推移转到其他的AppDomain中。 从安全的角度来看,一个受托管的线程的主用户与底层的非托管线程中的windows主用户是无关的。 .NET运行库监控着由.NET代码创建的所有线程,它还监控着所有非托管线程.这些非托管线程可能执行了托管代码。因为托管代码可以由随时可用的COM包装器提供,所以非托管线程能够很好地在.NET运行库中工作。 当非托管代码确实在托管线程中执行时,运行库就会检查TLS以査明是否存在一个托管Thread对象。如果发现了一个托管线程,运行库就会使用那个线程。如果没有发现托管线程,则运行库就会创建一个并使用它。这个过程非常简单,却非常必要。无论线程来自何处我们仍旧想得到它的一个对象表示。如果运行库不为这些类型的内部调用管埋和创建线程的话,那么我们就不能在托管环境中标识线程,甚至是控制它。 总结
以上是内存溢出为你收集整理的VB.net学习笔记(二十四)线程基础全部内容,希望文章能够帮你解决VB.net学习笔记(二十四)线程基础所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)