一、 Thread類的基本用法
通過System.Threading.Thread類可以開始新的線程,並在線程堆棧中運行靜態或實例方法。可以通過Thread類的的構造方法傳遞一個無參數,並且不返回值(返回void)的委託(ThreadStart),這個委託的定義如下:
[ComVisibleAttribute(true)]
public delegate void ThreadStart()
我們可以通過如下的方法來建立並運行一個線程。
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
namespace MyThread
{
class Program
{
public static void myStaticThreadMethod()
{
Console.WriteLine("myStaticThreadMethod");
}
static void Main(string[] args)
{
Thread thread1 = new Thread(myStaticThreadMethod);
thread1.Start(); // 只要使用Start方法,線程纔會運行
}
}
}
除了運行靜態的方法,還可以在線程中運行實例方法,代碼如下:
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
namespace MyThread
{
class Program
{
public void myThreadMethod()
{
Console.WriteLine("myThreadMethod");
}
static void Main(string[] args)
{
Thread thread2 = new Thread(new Program().myThreadMethod);
thread2.Start();
}
}
}
如果讀者的方法很簡單,或出去某種目的,也可以通過匿名委託或Lambda表達式來爲Thread的構造方法賦值,代碼如下:
thread3.Start();
Thread thread4 = new Thread(( ) => { Console.WriteLine("Lambda表達式"); });
thread4.Start();
其中Lambda表達式前面的( )表示沒有參數。
爲了區分不同的線程,還可以爲Thread類的Name屬性賦值,代碼如下:
thread5.Name = "我的Lamdba";
thread5.Start();
如果將上面thread1至thread5放到一起執行,由於系統對線程的調度不同,輸出的結果是不定的,如圖1是一種可能的輸出結果。
圖1
二、 定義一個線程類
我們可以將Thread類封裝在一個MyThread類中,以使任何從MyThread繼承的類都具有多線程能力。MyThread類的代碼如下:
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
namespace MyThread
{
abstract class MyThread
{
Thread thread = null;
abstract public void run();
public void start()
{
if (thread == null)
thread = new Thread(run);
thread.Start();
}
}
}
可以用下面的代碼來使用MyThread類。
class NewThread : MyThread
{
override public void run()
{
Console.WriteLine("使用MyThread建立並運行線程");
}
}
static void Main(string[] args)
{
NewThread nt = new NewThread();
nt.start();
}
我們還可以利用MyThread來爲線程傳遞任意複雜的參數。詳細內容見下節。
三、 爲線程傳遞參數
Thread類有一個帶參數的委託類型的重載形式。這個委託的定義如下:
[ComVisibleAttribute(false)]
public delegate void ParameterizedThreadStart(Object obj)
這個Thread類的構造方法的定義如下:
下面的代碼使用了這個帶參數的委託向線程傳遞一個字符串參數:
{
Console.WriteLine(obj);
}
static void Main(string[] args)
{
Thread thread = new Thread(myStaticParamThreadMethod);
thread.Start("通過委託的參數傳值");
}
要注意的是,如果使用的是不帶參數的委託,不能使用帶參數的Start方法運行線程,否則系統會拋出異常。但使用帶參數的委託,可以使用thread.Start()來運行線程,這時所傳遞的參數值爲null。
也可以定義一個類來傳遞參數值,如下面的代碼如下:{
private String d1;
private int d2;
public MyData(String d1, int d2)
{
this.d1 = d1;
this.d2 = d2;
}
public void threadMethod()
{
Console.WriteLine(d1);
Console.WriteLine(d2);
}
}
MyData myData = new MyData("abcd",1234);
Thread thread = new Thread(myData.threadMethod);
thread.Start();
如果使用在第二節定義的MyThread類,傳遞參數會顯示更簡單,代碼如下:
{
private String p1;
private int p2;
public NewThread(String p1, int p2)
{
this.p1 = p1;
this.p2 = p2;
}
override public void run()
{
Console.WriteLine(p1);
Console.WriteLine(p2);
}
}
NewThread newThread = new NewThread("hello world", 4321);
newThread.start();
四、 前臺和後臺線程
使用Thread建立的線程默認情況下是前臺線程,在進程中,只要有一個前臺線程未退出,進程就不會終止。主線程就是一個前臺線程。而後臺線程不管線程是否結束,只要所有的前臺線程都退出(包括正常退出和異常退出)後,進程就會自動終止。一般後臺線程用於處理時間較短的任務,如在一個Web服務器中可以利用後臺線程來處理客戶端發過來的請求信息。而前臺線程一般用於處理需要長時間等待的任務,如在Web服務器中的監聽客戶端請求的程序,或是定時對某些系統資源進行掃描的程序。下面的代碼演示了前臺和後臺線程的區別。{
Thread.Sleep(3000);
}
Thread thread = new Thread(myStaticThreadMethod);
// thread.IsBackground = true;
thread.Start();
如果運行上面的代碼,程序會等待3秒後退出,如果將註釋去掉,將thread設成後臺線程,則程序會立即退出。
要注意的是,必須在調用Start方法之前設置線程的類型,否則一但線程運行,將無法改變其類型。
通過BeginXXX方法運行的線程都是後臺線程。