C#是.Net平台的通用開發工具,它能夠建造所有的.Net應用。在.Net中所有線程都運行在應用程序域(AppDomain)中,這也許讓你想到Win32進程,實際上它們還是有很大的不同。應用程序域提供了一種安全而通用的處理單元,公共語言運行庫可使用它來隔離應用程序。注意在.Net中應用程序的隔離是應用程序域而不是進程,在單個進程中可以存在幾個應用程序域,而且線程可以跨越應用程序域的范圍,某個線程中的方法可以調用另一個線程的方法,這樣的話就不會造成進程間調用或進程間切換等方面的額外開銷。可以說應用程序域是物理進程(也即win32中的Process)內的邏輯進程。
在Visul C#中System.Threading 命名空間提供一些使得可以進行多線程編程的類和接口,其中線程的創建有以下三種方法:Thread、ThreadPool、Timer。下面我就它們的使用方法逐個作一簡單介紹。
1. Thread
這也許是最復雜的方法,但它提供了對線程的各種靈活控制。首先你必須使用它的構造函數創建一個線程實例,它的參數比較簡單,只有一個ThreadStart 委托:
[C#]
public Thread(ThreadStart start);
然後調用Start()啟動它,當然你可以利用它的Priority屬性來設置或獲得它的運行優先級(enum ThreadPriority: Normal、 Lowest、 Highest、 BelowNormal、 AboveNormal)。見下例:它首先生成了兩個線程實例t1和t2,然後分別設置它們的優先級,接著啟動兩線程(兩線程基本一樣,只不過它們輸出不一樣,t1為“1”,t2為“2”,根據它們各自輸出字符個數比可大致看出它們占用CPU時間之比,這也反映出了它們各自的優先級)。
static void Main(string[] args)
{
Thread t1 = new Thread(new ThreadStart(Thread1));
Thread t2 = new Thread(new ThreadStart(Thread2));
t1.Priority = ThreadPriority.BelowNormal ;
t2.Priority = ThreadPriority.Lowest ;
t1.Start();
t2.Start();
}
public static void Thread1()
{
for (int i = 1; i < 1000; i++)
{//每運行一個循環就寫一個“1”
dosth();
Console.Write("1");
}
}
public static void Thread2()
{
for (int i = 0; i < 1000; i++)
{//每運行一個循環就寫一個“2”
dosth();
Console.Write("2");
}
}
public static void dosth()
{//用來模擬復雜運算
for (int j = 0; j < 10000000; j++)
{
int a=15;
a = a*a*a*a;
}
}
以上程序運行結果為:
11111111111111111111111111111111111111111121111111111111111111111111111111111111111112
11111111111111111111111111111111111111111121111111111111111111111111111111111111111112
11111111111111111111111111111111111111111121111111111111111111111111111111111111111112
從以上結果我們可以看出,t1線程所占用CPU的時間遠比t2的多,這是因為t1的優先級比t2的高,若我們把t1和t2的優先級都設為Normal,那結果是如何?它們所占用的CPU時間會一樣嗎?是的,正如你所料,見下圖:
121211221212121212121212121212121212121212121212121212121212121212121
212121212121212121212121212121212121212121212121212121212121212121212
121212121212121212
從上例我們可看出,它的構造類似於win32的工作線程,但更加簡單,只需把線程要調用的函數作為委托,然後把委托作為參數構造線程實例即可。當調用Start()啟動後,便會調用相應的函數,從那函數第一行開始執行。
接下來我們結合線程的ThreadState屬性來了解線程的控制。ThreadState是一個枚舉類型,它反映的是線程所處的狀態。當一個Thread實例剛創建時,它的ThreadState是Unstarted;當此線程被調用Start()啟動之後,它的ThreadState是 Running; 在此線程啟動之後,如果想讓它暫停(阻塞),可以調用Thread.Sleep() 方法,它有兩個重載方法(Sleep(int )、Sleep(Timespan )),只不過是表示時間量的格式不同而已,當在某線程內調用此函數時,它表示此線程將阻塞一段時間(時間是由傳遞給 Sleep 的毫秒數或Timespan決定的,但若參數為0則表示掛起此線程以使其它線程能夠執行,指定 Infinite 以無限期阻塞線程),此時它的ThreadState將變為WaitSleepJoin,另外值得注意一點的是Sleep()函數被定義為了static?! 這也意味著它不能和某個線程實例結合起來用,也即不存在類似於t1.Sleep(10)的調用!正是如此,Sleep()函數只能由需“Sleep”的線程自己調用,不允許其它線程調用,正如when to Sleep是個人私事不能由它人決定。但是當某線程處於WaitSleepJoin狀態而又不得不喚醒它時,可使用Thread.Interrupt 方法 ,它將在線程上引發ThreadInterruptedException,下面我們先看一個例子(注意Sleep的調用方法):
static void Main(string[] args)
{
Thread t1 = new Thread(new ThreadStart(Thread1));
t1.Start();
t1.Interrupt ();
E.WaitOne ();
t1.Interrupt ();
t1.Join();
Console.WriteLine(“t1 is end”);
}
static AutoResetEvent E = new AutoResetEvent(false);
public static void Thread1()
{
try
{//從參數可看出將導致休眠
Thread.Sleep(Timeout.Infinite);
}
catch(System.Threading.ThreadInterruptedException e)