细说.NET 中的多线程:概念

为什么使用多线程

使用户界面能够随时相应用户输入

当某个应用程序在进行大量运算时候,为了保证应用程序能够随时相应客户的输入,这个时候我们往往需要让大量运算和相应用户输入这两个行为在不同的线程中进行。

效率原因

应用程序经常需要等待一些资源,如等待网络资源,等待io资源,等待用户输入等等。这种情况下使用多线程可以避免CPU长时间处于闲置状态。

用户态,内核态

线程内的资源有两种运行态,即用户态和内核态。某些运算可以在堆栈上进行,这种情况线程是在用户态运行的,某些需要高权限运行的指令,或者某些优先级很高的指令需要在操作系统内核中进行,这个时候线程会运行在内核态。出于安全原因,用户态和内核态的资源是不能够互相访问的,因此在用户态和内核态的切换过程中,我们需要进行相关上下文以及变量的复制,这意味的用户态和内核态的切换是以一定的时间消耗为代价的。

由于CPU是以时间片为单位进行线程的切换的,由于CPU的运算速度远大于内存的读写速度,因此CPU和内存之间通常有两级缓存,不同的线程的上下文访问的数据往往是不同的,这样线程的切换需要经常频繁的切换CPU缓存的内容,也需要更新线程的调度信息,这些都是需要花费一定的时间的,因此合理的使用多线程,来避免CPU不停的进行上下文切换。

System.Thread介绍 创建一个线程

创建每一个线程的时候,CLR都需要进行一系列的操作,如初始化线程的本地资源,为线程分配用户模式和内核模式下相应的堆栈,加载相应的托管,非托管资源等。

最简单常用的创建线程的方式是使用ThreadStart来创建线程,相关代码如下:

细说.NET 中的多线程:概念

ThreadStart只需要一个委托即可,如果你善于使用匿名方法,也可以用匿名方法来代替委托,使用匿名方法的另一个好处是可以通过匿名方法的闭包特性来为新的线程传递参数。

虽然使用匿名方法的闭包特性可以很方便的为线程传递参数,但是也往往会带来一些不容易发现的问题,如下面的程序,由于i变量的共享,在运行的时候输出会有问题:

细说.NET 中的多线程:概念

正确的写法应该是这样的:

细说.NET 中的多线程:概念

线程异常的捕获

如果线程中可能需要捕获异常,那么我们不能这样做:

细说.NET 中的多线程:概念

而是这样做:

细说.NET 中的多线程:概念

System.Thread线程的成员

System.Threading.Thread帮助我们实现了一些线程的基本操作,如:

属性名称

 

说明

 

CurrentContext

 

获取线程正在其中执行的当前上下文。

 

CurrentThread

 

获取当前正在运行的线程。

 

ExecutionContext

 

获取一个 ExecutionContext 对象,该对象包含有关当前线程的各种上下文的信息。

 

IsAlive

 

获取一个值,该值指示当前线程的执行状态。

 

IsBackground

 

获取或设置一个值,该值指示某个线程是否为后台线程。

 

IsThreadPoolThread

 

获取一个值,该值指示线程是否属于托管线程池。

 

ManagedThreadId

 

获取当前托管线程的唯一标识符。

 

Name

 

获取或设置线程的名称。

 

Priority

 

获取或设置一个值,该值指示线程的调度优先级。

 

ThreadState

 

获取一个值,该值包含当前线程的状态。

 

方法名称

 

说明

 

Abort()    

 

终止本线程。

 

GetDomain()

 

返回当前线程正在其中运行的当前域。

 

GetDomainId()

 

返回当前线程正在其中运行的当前域Id

 

Interrupt()

 

中断处于 WaitSleepJoin 线程状态的线程。

 

Join()

 

已重载。阻塞调用线程,直到某个线程终止时为止。

 

Resume()

 

继续运行已挂起的线程。

 

Start()  

 

执行本线程。

 

Suspend()

 

挂起当前线程,如果当前线程已属于挂起状态则此不起作用

 

Sleep()  

 

把正在运行的线程挂起一段时间。

 

前台线程vs后台线程

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/0c8818ca9a340863fd1e88bc97b530fa.html