<五> 线程常用类
5.1 Thread类
System.Threading.Thread类是创建并控制线程,设置其优先级并获取其状态最为常用的类。Thread类的公共属性如下表。
Thread类的公共属性
属性 |
说明 |
ApartmentState |
获取或设置引线程的单元状态。 |
CurrentContext |
获取线程正在其中执行的当前下下文。 |
CurrentCulture |
获取或设置当前线程的区域性。 |
CurrentPrincipal |
获取或设置线程的当前负责人(对基于角色的安全性而言) |
CurrentThread |
获取当前正在运行的线程 |
CurrentUICulture |
获取或设置资源管理器使用的当前区域性以便在运行时查找区域性特定的资源 |
ExecutionContext |
获取一个ExecutionContext对象,该对象包含有关当前线程的各种上下文的信息 |
IsAlive |
获取一个值,该值表示当前线程的执行状态。如果此线程已启动并且尚未正常终止或中止,则为true,否则为false. |
IsBackground |
获取或设置一个值,该值批示某个线程是否为后台线程 |
IsThreadPoolThread |
获取一个值,该值指示线程是否属于托管线程池 |
ManagedThreadId |
获取或设置线程的惟一标识 |
Name |
获取或设置线程的名称。 |
Priority |
获取或设置一个值,该值指示线程的调度优先级 |
ThreadState |
获取一个值,该值包含当前线程的状态。 |
在Thread类中,Priority是一个很重要的属性,它用于获取或设置任何线程的优先级。System.Threading.Thread.Priority枚举了线程的优先级别,从而决定了线程能够得到多少CPU时间。高优先级的线程通常会比一般优先级的线程得到更多的CPU时间,如果不止一个高优先级的线程,操作系统将在这些线程之间循环分配CPU时间;低优先级的线程得到的CPU时间相对较少,当没有高优先级的线程时,操作系统将挑选下一个低优先级的线程执行。一旦低优先级线程在执行时遇到了高优先级的线程,这让出CPU给高优先级的线程。新创建线程的优先级一般优先级,可以设置线程的优先级别如下表。
成员名称 |
说明 |
Highest |
优先级别最高,可以将线程安排在任何其他优先级的线程之前。 |
AboveNormal |
优先级别次高,可以将线程安排在Highest优先级别之后,在具有Normal优先级别之前。 |
Normal |
优先级别普通,可以将线程安排在AboveNormal优先级别之后,在具有BelowNorml优先级别之前。 |
BelowNormal |
优先级别次低,可以将线程安排在Normal优先级别之后,在具有Lowest优先级别之前。 |
Lowest |
优先级别最低,可以将线程安排在任何其它优先级的线程之后。 |
System.Threading.Thread.ThreadState属性定义了执行时的线程状态,线程从创建到终止,一定处于其中一个状态。当线程创建时,它处于Unstarted状态。Thread类的Start()方法将使其变为Running状态,线程将一直处于这样的状态;除非我们调用相应的方法使其挂起、阻塞或者自然终止。如果线程挂起,它将处于Suspended状态;除非我们调用resume()方法使其重新执行,这时线程将重新变为Running状态。一旦线程被销毁或者终止,则处于Stopped状态,处于这个状态的线程将不复存在。正如线程开始启动,则不可能回到Unstarted状态。线程还有一个Background状态,它它表明线程运行在前台不定式是后台。在一个确定的时间,线程可能处于多个状态。举例来说,一个线程被调用了Sheep而处于阻塞,而接着另外一个线程通过Abort方法调用这个阻塞的线程,这时线程将同时处于WaitSleepJoin和AbortRequested状态。一旦线程响应转为Sle阻塞或者中止,当销毁 时会抛出ThreadAbortException异常。
Thread类的公共方法
方法 |
说明 |
Abort |
在调用此方法的线程上引发ThreadAbortExcetion,以开始终止线程。调用此方法通常会终止线程。 |
AllocateDataSlot |
在所有线程上分配未命名的数据槽。 |
AllocateNamedDataSlot |
在所有线程上分配已命名的数据槽。 |
BeginCriticalRegion |
通知宿主执行将要进行一个代码区域,在该代码区域线程中止或未处理异常的影响可能会危害应用程序域中的其他任务。 |
BeginThreadAffinity |
通知宿主托管代码将要执行领带于当前物理操作系统线程的标识指令。 |
EndThreadAffinity |
通知宿主托管代码已执行领带于当前物理操作系统线程的标识的指令 |
Equals |
已重载,确定两个Object实体是否相等。 |
FreeNamedDataSlot |
为进程中的所有线程消除名称与槽之间的关联。 |
GetApartmentState |
返回一个ApartmentState值,该值指示单元状态。 |
GetCompressedStack |
返回一个ApartmentState值,该值指示单元状态 |
GetData |
在当前线程的当前域中从当前线程上指定的槽中检索值。 |
GetDomain |
返回当前线程正在其中运行的当前域。 |
GetDomainID |
返回惟一的应用程序域标识符。 |
GetHashCode |
返回唯一的应用程序的哈希代码。 |
GetNameDataSlot |
查找已命名的数据槽。 |
GetType |
获取当前实例的Type |
Interrupt |
中断处于WaitSleepJoin线程状态的线程。 |
Join |
阻塞调用线程,直到某个线程终止时为止。 |
MemoryBarrier |
按如下方式同止访问内存,即执行当前线程的处理器在对指令重新排序时,不能采用先执行MemoryBarrier调用之后的内存访问,在执行MemoryBarrier调用之前的内存访问的方式。 |
RefernceEquals |
确定指定的Object实例是否是相同的实例。 |
ResetAbort |
取消为当前线程请求的Abort |
Resume |
继续已挂起的线程。 |
SetApartmentState |
对当前线程应用捕获的CompressedStack |
SetData |
在当前正在运行的线程上为此线程的当前域在指定槽中设置数据。 |
Sleep |
将当前线程阻塞指定的毫秒数。 |
SpinWait |
导致线程等待由iterations参数定义的时间量 |
Start |
使线程得以按计划执行 |
Suspend |
挂起线程,或者如果线程已挂起,则不起作用。 |
TrySetApartmentState |
在线程启动前设置其单元状态。 |
VolatileRead |
读取字段值,无论处理器的数目或处理器缓存的状态如何,该值是由计算机的任何处理器写入的最新值。 |
VolatileWrite |
立即向字段中写入一个值,以使该值对计算机中的所有处理器都可见。 |
5.2 Mutex类
当两个或更多线程需要同访问一个共享资源时,系统需要使用同步机制来确保一次只有一个线程使用该资源。Mutex是同步基元,它只向一个线程授予对共享资源的独占访问权。
如果一个线程获取了互斥,则要获取该互斥体的第2个线程挂起,直到第1个线程释放该互斥体。Mutex类比Monitor类使用更多的系统资源,但是它可以跨应用程序域边界进行封送处理,可用于多个等待并同步不同进程中的线程。
一个线程可以通过调用WaitHandle.WaitOne或WaitHandle.WaitAny或WaitHandle.WaitAll得到Mutex的拥有权。如果Mutex不属于任何线程,上述调用将使得线程拥有Mutex ,而且WaitOne会立即返回。但是如果有其他线程拥有Mutex,WaitOne将陷入无限期的等待,直到获取Mutex。可以在WaitOne方法中指定参数,即等待的时间而避免无限期的等待,调用Close作用于Mutex 将释放拥有。一旦Mutex 被创建,即可通过GetHandle方法获得Mutex的句柄,而给WaitHandle.WaitAny或WaithHandle.WaitAll方法使用。
Mutex公共方法
方法 |
说明 |
Close |
在派生类中被重写时,释放由当前WaitHandle持有的所有资源。 |
CreateObjRef |
创建一个对象,该对象包含生成用于与远程对象通信代理所需的全部相关信息。 |
ReleaseMutex |
释放Mutex一次。 |
WaitOne |
当在派生类中重写时,阻塞当前线程,直到当前WaitHandle收到信号。 |
使用ReleaseMutex()方法释放Mutex一次,拥有互斥体的线程可以在重复等待函数调用中指定相同的互斥体而不用阻塞其执行,调用次数由运行库保存。线程必须调用与ReleaseMutex方法相同的次数以释放互斥体的所属权。如果线程在拥有互斥体期间正常终止,则互斥体状态设置为终止,并且下一个等待线程获得所属权。如果没有线程拥有互斥体,则互斥体状态为终止。
示例:
Using System;
Using System.Threading;
Class Test
{
Private static Mutex mut=new Mutex(); //创建一个Mutex对象
Private const int numIterations=1;
Private const int numThreads=3; //设置线程数量为3
Static void Main()
{
//创建线程,该线程将访问共享资源
for (int i=0;i<numThreadings;i++)
{
//创建线程
Thread myThread=new Thread(new ThreadStart(MyThreadProc));
myThread.Name=String.Format(“Thread{0}”,i+1);
myThread.Start(); //启动线程
}
}
Private static void MyThreadProc()
{
for (int i=0;i<numIterations;i++)
{
UseResource(); //使用共享资源。
}
}
Private static void UseResource()
{
//阻塞当前线程,直到线程安全进入
mut.WaitOne();
Console.WriteLine(“{0}已进行保护区域”,Thread.CurrentThread.Name);
//模拟一些工作
//线程阻塞
Thread.Sleep(500);
Console.WriteLine(“{0}正在离开保护区域”,Thread.CurrentThread.Name);
mut.ReleaseMutex(); //释放Mutex对象
}
}
5.3 ReaderWriterLock类
ReaderWriterLock类定义支持单个写线程和多个读线程锁,用于同步对资源的访问。在任一特定时刻,它允许多个线程同时进行读访问,或者允许单个线程进行写访问。在资源不经常发生更改情况下,ReaderWriterLock类所提供的吞吐量比简单的一次只允许一个线程的锁更高。
ReaderWriterLock类支持以下功能。
(1) 大量用于针对每个对象的同步活动中。
(2) 超时用来检测死锁。
(3) 事件缓存可以使用时间从争议最小的区域移动到争议最大的区域,ReaderWirterLock需要的事件数由进程中的线程数限定。
(4) 由阅读器和编写器嵌套的锁。
(5) 用于避免在多处理器计算机上进行上下文切换的旋转计数。
(6) 编写器锁的升级和降级,升级到编写器锁将返回指示中间写入的参数,而从编写器锁降到还原锁的状态。
(7) 用于释放锁以便线程可以调用应用程序代码的方法,还原锁的状态并指示中间写入。
(8) 从最常见的失败(如创建事件)中恢复,锁将维护一致的内部状态并且保持可用。
ReaderWriterLock公共属性
属性 |
说明 |
IsReaderLockHeld |
获取一个值,指示当前线程是否持有读线程锁。 |
IsWriterLockHeld |
获取一个值,指示当前线程是否持有写线程锁。 |
WriterSeqNum |
获取当前序列号。 |
ReaderWriterLock公共方法
方法 |
说明 |
AcquireReaderLock |
获取读线程锁 |
AcquireWriterLock |
获取写线程锁 |
AnyWritersSince |
指示获取序列号是否已将写线程锁授予线程 |
DowngradeFromWriterLock |
将线程的锁状态还原为调用UpgradeToWriterLock前的状态 |
ReleaseLock |
释放锁,忽略线程获取锁的次数如何 |
ReleaseReaderLock |
减少锁计算 |
ReleaseWriterLock |
减少写线程锁不的锁计数 |
RestoreLock |
将线程的锁状态还原为ReleaseLock前的状态 |
UpgradeToWriterLock |
将读线程锁升级为写线程锁。 |
5.4 ThreadPool类
如果有多个任务需要完成,每个任务需要一个线程,这时应该考虑使用线程池来更有效地管理计算机资源并且从中受益。线程池是执行的多个线程集合,它允许系统添加以线程自动创建和开始的任务到队列中,使用线程池使得系统可以优化线程在CPU使用时的时间碎片。但是要记住在任何特定的时间点,每一个进程一每个线程池只有一个个正在运行的线程。使用ThreadPool类可以使得由线程组成的池可以被系统管理,而使开发人员主要精力集中在工作流的逻辑,而不是线程管理上。
ThreadPool类提供了一个线程池,该线程池通用于发送工作项、处理异步I/O、代表其他线程等待,以及处理计时器。很多应用程序创建的线程都要在休眠状态中消耗大量的时间以等待事件的发生,其他线程可能进行休眠状态,只被定期唤醒以轮询更改或更新状态信息。
TheadePool类的公共方式
方法 |
说明 |
BindHandle |
将操作系统句柄绑定到ThreadPool |
GetAvailableThreads |
检索由GetMaxThreads返回线程池线程的最大数目和当前活动数目之间的差值 |
GetMaxThreads |
检索可以同时处于活动状态的线程池请求的数目,所有大于此数目的请求将保持排队状态,直到线程池线程变为可用。 |
GetMinThreads |
检索ThreadPool在新请求预测中维护的空闲线程数 |
QueueUserWorkItem |
将方法排入队列以便执行,此方法在有线程池线程变得可用时执行。 |
RegisterWaitForSingleObject |
注册正在等待WaitHandle的委托 。 |
SetMinThreads |
设置ThreadPool在新请求预测中维护的空间线程数。 |
UnsafeQueueUserWorkItem |
将用户工作项排队线程池 |
UnsafeRegisterWaitForSingleOjbect |
将指定的委托排队线程池。 |
示例:
Public class Example
{
Public static void Main()
{
//唤醒线程,并将用户工作项排队到线程池
ThreadPool.QueueUserWorkItem(new WaitCallback(ThreadProc));
……
}
Static void ThreadPro(Object stateInfo)
{
Console.WriteLine(“Hell from the thread pool”);
}
}
5.5 WaitHandle类
WaitHandle类封装等待共享资源的独占访问权的操作系统特定的对象,通常用做同步对象的基类。从该类派生的类定义一个信号传输机制以指示获取或释放对共享资源的独占访问,但使用继承的WaitHandle方法在等待对共享资源的访问时阻塞。使用此类的静态方法阻塞刈割线程,直到一个或多个同步对象接收到信息。
5.5 AutoResetEent类
AutoResetEvent类通知正在等待的线程已发生事件,无法继承此类。AutoResetEvent类允许线程通过发信号互相通信,通常此通信涉及线程需要独占访问的资源。
AutoResetEvent类将始终保持终止,直到一个正在等待的线程被释放,此时系统将自动把状态设置为非终止状态。如果没有任何线程丰等待,则状态将保持为终止状态。