1.线程操作
System.Threading.Thread类是创建并控制线程,设置其优先级并获取其状态最为常用的类。该类以对象的方式封装了特定应用程序域中给定的程序执行路径,类中提供许多线程操作的常用方法。
Thread类的常用属性
属 性 名 | 描 述 |
CurrentThread | 只读属性,获取当前正在运行的线程 |
IsAlive | 判断线程是否处于活动状态 |
IsBackground | 获取或设置一个值,该值指示某个线程是否为后台线程 |
IsThreadPoolThread | 获取一个值,该值指示线程是否属于托管线程池 |
Name | 获取或设置线程的名称 |
Priority | ThreadPriority枚举类型,代表线程的优先级,如:Normal、AboveNormal、BelowNormal、Highest、Lowest |
ThreadState | ThreadState枚举类型,代表当前线程的状态,如:Unstarted、Running、WaitSleepJoin、Stopped、AbortRequested、Suspended、Aborted等 |
Thread类的常用方法
方 法 名 | 描 述 |
GetDomain( ) | 返回当前线程所在的应用程序域 |
GetDomainID( ) | 返回当前线程所在的应用程序域的ID |
Start( ) | 启动线程的执行 |
Suspend( ) | 挂起线程,或者如果线程已挂起,则不起作用 |
Resume( ) | 继续已挂起的线程 |
Interrupt( ) | 终止处于Wait或者Sleep或者Join线程状态的线程 |
Join( ) | 阻塞调用线程,直到某个线程终止时为止 |
Sleep( ) | 将当前线程阻塞指定的毫秒数 |
Abort( ) | 终止一个线程的运行。如果线程已经终止,则不能通过Thread.Start( )来启动线程 |
线程的控制操作一般包括以下几个方面:
(1)创建线程
创建一个线程就是实例化一个Thread类的对象,Thread类的构造函数带有一个ThreadStart类型的参数,这是一个委派用于传递线程的入口方法,创建ThreadStart对象时需要一个静态方法或实例方法作为参数。 (2)启动线程
启动线程很简单,只需要调用Thread类的Start方法。
(3)休眠线程
线程的休眠是让当前的线程进入一定时间的休眠状态,时间一到线程将继续执行。通过Thread类的Sleep方法来实现线程的休眠。Thread类中有两个重载的Sleep方法,一个带有int类型的参数,用于指定休眠的毫秒(ms)数,另一个带有TimeSpan类型的参数,指定休眠的时间段。 示例如下:
Thread.Sleep(1000);//线程休眠1000毫秒
TimeSpan WaitTime = new TimeSpan(0,0,0,0,1000);
Thread.Sleep(WaitTime);//线程休眠按天 小时 分钟 秒 毫秒计算
(4)挂起线程
线程的挂起是暂停线程,如果不再启动线程,它将永远保持暂停状态。只有当前运行的线程才可以被挂起,对已经挂起的线程实施挂起没有作用,因此在使用Supend方法前,一般要先检查该线程是否正在运行。通常是查询Thread的ThreadState属性值。 示例如下:
if (t1.ThreadState == ThreadState.Running)//判断线程是否正在运行
t1.Suspend( );
(5)继续线程
已经挂起的线程可以使用Thread类的Resume方法继续运行。如果没有被挂起的线程使用该操作将不起作用,所以使用Resume方法前,一般也先判断线程是否已经被挂起。示例如下: if (t1.ThreadState == ThreadState.Suspended)//判断线程是否已被挂起。
t1.Resume( );
(6)终止线程
在终止线程之前,一般先判断线程的IsAlive属性,确认该线程是否处于活动状态,处于活动状态的线程才可以使用Thread类的Abort方法进行终止。示例如下: if (t1.IsAlive)//判断线程是否处于活动状态
t1.Abort( );
2.线程同步
在包含多个线程的应用程序中,线程间有时会共享存储空间,当两个或多个线程同时访问同一共享资源时,必然会出现冲突问题。如一个线程可能尝试从一个文件中读取数据,而另一个线程则尝试在同一个文件中修改数据。在这种情况下,数据可能变得不一致。针对这种问题,通常需要让一个线程彻底完成其任务后,再运行下一个线程;或者要求一个线程对共享资源访问完全结束后,再让另一个线程访问该资源,必须保证一个共享资源一次只能被一个线程使用。实现此目的的过程称为线程同步。
在C#.NET中提供了多种实现线程同步的方法。如加锁(Lock)、监视器(Monitor)、互斥体(Mutex)等。
(1)加锁(Lock)
实现多线程同步的最直接办法就是加锁,就像服装店的试衣间一样,当一个顾客进去试衣时把试衣间门锁上,其他顾客必须等他出来后才能进去试衣。C#语言的lock语句就可以实现这个功能。它可以把一段代码定义为互斥段,在一个时刻内只允许一个线程进入执行,而其他线程必须等待。 其基本格式如下: lock(expression)statement_block
其中expression代表要加锁的对象,必须是引用类型。一般地,如果要保护一个类的实例成员,可以使用this;如果要保护一个静态成员,或者要保护的内容位于一个静态方法中,可以使用类名,
格式为:lock(typeof(类名)){ }。
statement_block:代表共享资源,在一个时刻内只能被一个线程执行。
(2)监视器(Monitor)
Monitor的功能和lock有些相似,但是它比lock功能更灵活、更强大。Monitor相当于服装店试衣间的开门人,他管着试衣间的钥匙,而线程好比是要使用试衣间的顾客,他要进入试衣间之前,必须先从看门人手上获取钥匙,试衣出来以后,需要把钥匙还给看门人,看门人可以把它交给下一个正在等待进入试衣间的顾客。在这个过程中,顾客会出现3种状态,分别对应于多线程程序中线程的状态。 Monitor类的常用方法
方 法 名 | 描 述 |
Enter( ) | 获取锁定对象的钥匙,同时标记临界区的开头。如果此时正有另外一个线程掌管这把钥匙,该方法将进入阻塞状态,直到锁定的对象被打开,如果同一线程正掌管钥匙,再次调用Enter方法将不会阻塞 |
TryEnter( ) | 和Enter方法类似,不同的是,当对象正被其他线程使用时,该方法不会自动阻塞,而是返回一个false值表明失败 |
Exit( ) | 结束对一个对象的监视,即交出锁定对象的钥匙,同时标记受锁定对象保护的临界区的结尾 |
Pulse( ) | 该方法通知位于等待队列中的下一个线程进入就绪队列,一旦调用该方法的线程释放钥匙后,就绪队列的线程就可以获取钥匙 |
PulseAll( ) | 和Pulse方法相似,通知等待队列中的所有线程转移到就绪队列中 |
Wait( ) | 释放对象的锁,并阻止当前线程直到它重新获取该锁 |
3)互斥体(Mutex)
互斥体是通过只向一个线程授予对共享资源的独占访问权。如果一个线程获取了互斥体,则要获取该互斥体的第二个线程将被挂起,直到第一个线程释放该互斥体。在命名空间System.Threading中的Mutex类代表了互斥体,Mutex类继承于WaitHandle类,该类代表了所有的同步对象。Mutex类用WaitOne方法来请求互斥体的所有权,用ReleaseMutex方法来释放互斥体的所有权。这两个方法以及Mutex类的构造函数都有多种重载形式,具体如下表所示。
函 数 原 型 | 描 述 |
public Mutex( ) | 调用此构造函数相当于调用构造函数public Mutex(false),将互斥体的初始所属权指定为false,即调用线程不拥有互斥体所有权 |
public Mutex(bool initiallyOwned) | 如果initiallyOwned为false,则使用情况同上,如果为true,则表示调用线程拥有互斥体的初始所有权 |
public Mutex(bool initiallyOwned,string name) | 第一个参数使用情况同上。第二个参数表示用字符串name作为该互斥体的名称来初始化Mutex类的新实例,如果name值为空引用,则Mutex是未命名的 |
public Mutex(bool initiallyOwned,string name,out bool createdNew) | 前两个参数同上。第三个参数是用来在该方法返回时指示调用线程是否被授予了互斥体的初始所有权,如果调用线程被授予了互斥体的初始所有权,则方法返回时createdNew为true,否则为false |
public Mutex(bool initiallyOwned,string name,out bool createdNew,MutexSecurity mutexSecurity) | 前三个参数同上。第四个参数用于指示该已命名的互斥体的访问控制安全性 |
public virtual bool WaitOne( ) | 用于请求互斥体的所有权 |
public virtual bool WaitOne(TimeSpan timeout,bool exitContex) | 第一个参数用于指定等待时间,如果超过等待时间线程还没有获得互斥体,WaitOne方法将返回false。第二个参数用来指定在开始等待之前,是否先退出调用上下文所在的同步域(如果处于同步上下文中),exitContext为true,则等待之前先退出上下文的同步域,否则为false |
public virtual bool WaitOne(int millisecondsTimeout,bool exitContex) | |
public void ReleaseMutex( ) | 用于释放已经拥有的互斥体 |