以前在使用VB來實現多線程的時候,發現有一定的難度。雖然也有這樣那樣的方法,但都不盡人意,但在C#中,要編寫多線程應用程式卻相當的簡單。這篇文章將作簡要的介紹,以起到拋磚引玉的作用!
.NET將關於多線程的功能定義在System.Threading名字空間中。因此,要使用多線程,必須先聲明引用此名字空間(using System.Threading;)。
即使你沒有編寫多線程應用程式的經驗,也可能聽說過“啟動線程”“殺死線程”這些詞,其實除了這兩個外,涉及多線程方面的還有諸如“暫停線程”“優先順序”“掛起線程”“恢複線程”等等。下面將一個一個的解釋。
a.啟動線程
顧名思義,“啟動線程”就是建立並啟動一個線程的意思,如下代碼可實現:
Thread thread1 = new Thread(new ThreadStart( Count));
其中的 Count 是將要被新線程執行的函數。
b.殺死線程
“殺死線程”就是將一線程斬草除根,為了不白費力氣,在殺死一個線程前最好先判斷它是否還活著(通過 IsAlive 屬性),然後就可以調用 Abort 方法來殺死此線程。
c.暫停線程
它的意思就是讓一個正在啟動並執行線程休眠一段時間。如 thread.Sleep(1000); 就是讓線程休眠1秒鐘。
d.優先順序
這個用不著解釋了。Thread類中有一個ThreadPriority屬性,它用來設定優先權,但不能保證作業系統會接受該優先順序。一個線程的優先順序可分為5種:Normal, AboveNormal, BelowNormal, Highest, Lowest。具體實現例子如下:
thread.Priority = ThreadPriority.Highest;
e.掛起線程
Thread類的Suspend方法用來掛起線程,知道調用Resume,此線程才可以繼續執行。如果線程已經掛起,那就不會起作用。
if (thread.ThreadState = ThreadState.Running)
{
thread.Suspend();
}
f.恢複線程
用來恢複已經掛起的線程,以讓它繼續執行,如果線程沒掛起,也不會起作用。
if (thread.ThreadState = ThreadState.Suspended)
{
thread.Resume();
}
下面將列出一個例子,以說明簡單的線程處理功能。此例子來自於協助文檔。
using System;
using System.Threading;
// Simple threading scenario: Start a static method running
// on a second thread.
public class ThreadExample {
// The ThreadProc method is called when the thread starts.
// It loops ten times, writing to the console and yielding
// the rest of its time slice each time, and then ends.
public static void ThreadProc() {
for (int i = 0; i < 10; i++) {
Console.WriteLine("ThreadProc: {0}", i);
// Yield the rest of the time slice.
Thread.Sleep(0);
}
}
public static void Main() {
Console.WriteLine("Main thread: Start a second thread.");
// The constructor for the Thread class requires a ThreadStart
// delegate that represents the method to be executed on the
// thread. C# simplifies the creation of this delegate.
Thread t = new Thread(new ThreadStart(ThreadProc));
// Start ThreadProc. On a uniprocessor, the thread does not get
// any processor time until the main thread yields. Uncomment
// the Thread.Sleep that follows t.Start() to see the difference.
t.Start();
//Thread.Sleep(0);
for (int i = 0; i < 4; i++) {
Console.WriteLine("Main thread: Do some work.");
Thread.Sleep(0);
}
Console.WriteLine("Main thread: Call Join(), to wait until ThreadProc ends.");
t.Join();
Console.WriteLine("Main thread: ThreadProc.Join has returned. Press Enter to end program.");
Console.ReadLine();
}
}
此代碼產生的輸出類似如下內容:
Main thread: Start a second thread.
Main thread: Do some work.
ThreadProc: 0
Main thread: Do some work.
ThreadProc: 1
Main thread: Do some work.
ThreadProc: 2
Main thread: Do some work.
ThreadProc: 3
Main thread: Call Join(), to wait until ThreadProc ends.
ThreadProc: 4
ThreadProc: 5
ThreadProc: 6
ThreadProc: 7
ThreadProc: 8
ThreadProc: 9
Main thread: ThreadProc.Join has returned. Press Enter to end program.
1.每個表單都有自己的都在不同的線程上運行,如果需要在表單之間互動,就需要線上程之間互動。
2.當線程Sleep時,系統就退出執行隊列一段時間,當睡眠結束時,系統會產生一個時鐘中斷,從而
使線程回到執行隊列中,從而恢複線程的執行。
3.如果父線程先於子線程結束,那麼子線程將在父線程結束的同時被迫結束。Thread.Join()方法使父線程等待,直到子線程結束。
Abort()方法帶來的後果是不可恢複的終止線程。
4.起始線程可以稱之為主線程,如果所有的前台線程都停止了,那麼主線程可以終止,而所有的後台線程都將無條件終止。
後台線程跟前台線程只有一個區別,那就是後台線程不妨礙程式的終止。一旦一個進程所有的前台線程都終止後,CLR
將通過調用任意一個存活中的後台進程的Abort()方法來徹底終止進程。
6.掛起,睡眠(都可稱為--阻塞,暫停)
與Thread.Sleep 不同,Thread.Suspend 不會使線程立即停止執行。直到線程到達安全點之後它才可以將該線程掛起。如果線程尚
未啟動或已經停止,則它將不能掛起。調用 Thread.Resume 將使另一個線程跳出掛起狀態並使該線程繼續執行。
一個線程不能對另一個線程調用Sleep ,但是一個線程可以對另一個線程調用Suspend。
還可以使用許多其它的方式來阻塞線程。例如,可以通過調用 Thread.Join 使一個線程等待另一個線程(子線程)停止。使用
Monitor.Wait使一個線程等待訪問一個同步對象。
5.關鍵字lock可以把一段代碼定義為互斥段(critical section),互斥段在一個時刻內只允許一個線程進入執行,而其他線程必須等待。
多線程公用一個對象時,就不應該使用lock關鍵字了,這裡Monitor,Monitor提供了使線程共用資源的方案。
Monitor類可以鎖定一個對象,一個線程只有得到這把鎖才可以對該對象進行操作。
如:
Monitor.Enter(obj);
//現在oQueue對象只能被當前線程操縱了
Monitor.Exit(obj);
6.一個進程開始時至少會有一個主線程 ( 即主執行執行個體 ) ,這是在系統載入你的程式的時候所建立的主執行流程。
而訊息佇列則是與線程 ( Thread ) 相關的,在似win2k上一個線程有一個且只有一個訊息佇列 ( queue ) 與之相對應。
訊息佇列是在什麼時候產生的呢? 在似win2k系統上,從一開始建立線程就已經有了。
一個線程可以建立多個表單。統發送給這些視窗的訊息都統一發送到同一個 訊息佇列 中,幸虧訊息結構中有msg.hwnd指出該條訊息與
哪一個視窗相關, DispatchMessage() 函數就是依照這個保證訊息指派處理自動化而且不會出錯!
7.每個表單都屬於建立它的線程,在一線程中直接訪或間接問其它線程中的表單的方法將導致執行階段錯誤(VS2005)。
解決方案:使用表單從Control繼承而來的Control.Invoke(Delegate)方法。該方法將在建立表單的線程上執行委託指向的方法。
注意:在VS2003下,可以在一個線程中直接或間接調用另一個線程中的表單的方法,而不會導致執行階段錯誤。