日期:2009-06-27  浏览次数:20402 次

多线程是许多操作系统所具有的特性,它能大大提高程序的运行效率,所以多线程编程技术为编程者广泛关注。目前微软的.Net战略正进一步推进,各种相关的技术正为广大编程者所接受,同样在.Net中多线程编程技术具有相当重要的地位。本文我就向大家介绍在.Net下进行多线程编程的基本方法和步骤。



开始新线程


在.Net下创建一个新线程是非常容易的,你可以通过以下的语句来开始一个新的线程:

Thread thread = new Thread (new ThreadStart (ThreadFunc));
thread.Start ();


第一条语句创建一个新的Thread对象,并指明了一个该线程的方法。当新的线程开始时,该方法也就被调用执行了。该线程对象通过一个System..Threading.ThreadStart类的一个实例以类型安全的方法来调用它要调用的线程方法。

第二条语句正式开始该新线程,一旦方法Start()被调用,该线程就保持在一个“alive”的状态下了,你可以通过读取它的IsAlive属性来判断它是否处于“alive”状态。下面的语句显示了如果一个线程处于“alive”状态下就将该线程挂起的方法:

if (thread.IsAlive) {
thread.Suspend ();
}


不过请注意,线程对象的Start()方法只是启动了该线程,而并不保证其线程方法ThreadFunc()能立即得到执行。它只是保证该线程对象能被分配到CPU时间,而实际的执行还要由操作系统根据处理器时间来决定。

一个线程的方法不包含任何参数,同时也不返回任何值。它的命名规则和一般函数的命名规则相同。它既可以是静态的(static)也可以是非静态的(nonstatic)。当它执行完毕后,相应的线程也就结束了,其线程对象的IsAlive属性也就被置为false了。下面是一个线程方法的实例:

public static void ThreadFunc()
{
for (int i = 0; i < 10; i++) {
Console.WriteLine("ThreadFunc {0}", i);
}
}



前台线程和后台线程


.Net的公用语言运行时(Common Language Runtime,CLR)能区分两种不同类型的线程:前台线程和后台线程。这两者的区别就是:应用程序必须运行完所有的前台线程才可以退出;而对于后台线程,应用程序则可以不考虑其是否已经运行完毕而直接退出,所有的后台线程在应用程序退出时都会自动结束。

一个线程是前台线程还是后台线程可由它的IsBackground属性来决定。这个属性是可读又可写的。它的默认值为false,即意味着一个线程默认为前台线程。我们可以将它的IsBackground属性设置为true,从而使之成为一个后台线程。

下面的例子是一个控制台程序,程序一开始便启动了10个线程,每个线程运行5秒钟时间。由于线程的IsBackground属性默认为false,即它们都是前台线程,所以尽管程序的主线程很快就运行结束了,但程序要到所有已启动的线程都运行完毕才会结束。示例代码如下:

using System;
using System.Threading;
class MyApp
{
public static void Main ()
{
for (int i=0; i<10; i++) {
Thread thread = new Thread (new ThreadStart (ThreadFunc));
thread.Start ();
}
}
private static void ThreadFunc ()
{
DateTime start = DateTime.Now;
while ((DateTime.Now - start).Seconds < 5)
;
}
}


接下来我们对上面的代码进行略微修改,将每个线程的IsBackground属性都设置为true,则每个线程都是后台线程了。那么只要程序的主线程结束了,整个程序也就结束了。示例代码如下:

using System;
using System.Threading;
class MyApp
{
public static void Main ()
{
for (int i=0; i<10; i++) {
Thread thread = new Thread (new ThreadStart (ThreadFunc));
thread.IsBackground = true;
thread.Start ();
}
}
private static void ThreadFunc ()
{
DateTime start = DateTime.Now;
while ((DateTime.Now - start).Seconds < 5)
;
}
}


既然前台线程和后台线程有这种差别,那么我们怎么知道该如何设置一个线程的IsBackground属性呢?下面是一些基本的原则:对于一些在后台运行的线程,当程序结束时这些线程没有必要继续运行了,那么这些线程就应该设置为后台线程。比如一个程序启动了一个进行大量运算的线程,可是只要程序一旦结束,那个线程就失去了继续存在的意义,那么那个线程就该是作为后台线程的。而对于一些服务于用户界面的线程往往是要设置为前台线程的,因为即使程序的主线程结束了,其他的用户界面的线程很可能要继续存在来显示相关的信息,所以不能立即终止它们。这里我只是给出了一些原则,具体到实际的运用往往需要编程者的进一步仔细斟酌。

线程优先级


一旦一个线程开始运行,线程调度程序就可以控制其所获得的CPU时间。如果一个托管的应用程序运行在Windows机器上,则线程调度程序是由Windows所提供的。在其他的平台上,线程调度程序可能是操作系统的一部分,也自然可能是.Net框架的一部分。不过我们这里不必考虑线程的调度程序是如何产生的,我们只要知道通过设置线程的优先级我们就可以使该线程获得不同的CPU时间。

线程的优先级是由Thread.Priority属性控制的,其值包含:ThreadPriority.Highest、ThreadPriority.AboveNormal、ThreadPriority.Normal、ThreadPriority.BelowNormal和ThreadPriority.Lowest。从它们的名称上我们自然可以知道它们的优先程度,所以这里就不多作介绍了。

线程的默认优先级为ThreadPriority.Normal。理论上,具有相同优先级的线程会获得相同的CPU时间,不过在实际执行时,消息队列中的线程阻塞或是操作系统的优先级的提高等原因会导致具有相同优先级的线程会获得不同的CPU时间。不过从总体上来考虑仍可以忽略这种差异。你可以通过以下的方法来改变一个线程的优先级。

thread.Priority = ThreadPriority.AboveNormal;


或是:

thread.Priority = ThreadPriority.BelowNormal;


通过上面的第一句语句你可以提高一个线程的优先级,那么该线程就会相应的获得更多的CPU时间;通过第二句语句你便降低了那个线程的优先级,于是它就会被分配到比原来少的CPU时间了。你可以在一个线程开始运行前或是在它的运行过程中的任何时候改变它的优先级。理论上你还可以任意的设置每个线程的优先级,不过一个优先级过高的线程往往会影响到其他线程的运行,甚至影响到其他程序的运行,所以最好不要随意的设置线程的优先级。


挂起线程和重新开始线程


Thread类分别提供了两个方法来挂起线程和重新开始线程,也