轉載:http://www.knowsky.com/540518.htmlhtml
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類中hreadPRiority屬性,它用來設置優先級,但不能保證操做系統會接受該優先級。一個線程的優先級可分爲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);oop
for (int i = 0; i < 4; i++) {
Console.WriteLine("Main thread: Do some work.");
Thread.Sleep(0);
}ui
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();
}
}this
此代碼產生的輸出相似以下內容:
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.spa
在Visul C#中System.Threading 命名空間提供一些使得能夠進行多線程編程的類和接口,其中線程的建立有如下三種方法:Thread、ThreadPool、Timer。下面就它們的使用方法逐個做一簡單介紹。操作系統
1、Thread
這也許是最複雜的方法,但它提供了對線程的各類靈活控制。首先你必須使用它的構造函數建立一個線程實例,它的參數比較簡單,只有一個ThreadStart 委託: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,結果見下圖:
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)
{//中斷處理程序
Console.WriteLine (" 1st interrupt");
}
E.Set ();
try
{// 休眠
Thread.Sleep(Timeout.Infinite );
}
catch(System.Threading.ThreadInterruptedException e)
{
Console.WriteLine (" 2nd interrupt");
}//暫停10秒
Thread.Sleep (10000);
}
運行結果爲:1st interrupt
2nd interrupt
(10s後)t1 is end
從上例咱們能夠看出Thread.Interrupt方法能夠把程序從某個阻塞(WaitSleepJoin)狀態喚醒進入對應的中斷處理程序,而後繼續往下執行(它的ThreadState也變爲Running),此函數的使用必須注意如下幾點:
一、此方法不只可喚醒由Sleep致使的阻塞,並且對一切可致使線程進入WaitSleepJoin狀態的方法(如Wait和Join)都有效。如上例所示, 使用時要把致使線程阻塞的方法放入try塊內, 並把相應的中斷處理程序放入catch塊內。
二、對某一線程調用Interrupt, 如它正處於WaitSleepJoin狀態, 則進入相應的中斷處理程序執行, 若此時它不處於WaitSleepJoin狀態, 則它後來進入此狀態時, 將被當即中斷。若在中斷前調用幾回Interrupt, 只有第一次調用有效, 這正是上例我用同步的緣由, 這樣才能確保第二次調用Interrupt在第一個中斷後調用,不然的話可能致使第二次調用無效(若它在第一個中斷前調用)。你能夠把同步去掉試試,其結果極可能是: 1st interrupt
上例還用了另外兩個使線程進入WaitSleepJoin狀態的方法:利用同步對象和Thread.Join方法。Join方法的使用比較簡單,它表示在調用此方法的當前線程阻塞直至另外一線程(此例中是t1)終止或者通過了指定的時間爲止(若它還帶了時間量參數),當兩個條件(如有)任一出現,它當即結束WaitSleepJoin狀態進入Running狀態(可根據.Join方法的返回值判斷爲什麼種條件,爲true,則是線程終止;false則是時間到)。線程的暫停還可用Thread.Suspend方法,當某線程處於Running狀態時對它調用Suspend方法,它將進入SuspendRequested狀態,但它並不會被當即掛起,直到線程到達安全點以後它才能夠將該線程掛起,此時它將進入Suspended狀態。如對一個已處於Suspended的線程調用則無效,要恢復運行只需調用Thread.Resume便可。
最後咱們談的是線程的銷燬,咱們能夠對需銷燬的線程調用Abort方法,它會在此線程上引起ThreadAbortException。咱們可把線程內的一些代碼放入try塊內,並把相應處理代碼放入相應的catch塊內,當線程正執行try塊內代碼時如被調用Abort,它便會跳入相應的catch塊內執行,執行完catch快內的代碼後它將終止(若catch塊內執行了ResetAbort則不一樣了:它將取消當前Abort請求,繼續向下執行。因此如要確保某線程終止的最好用Join,如上例)。
2、ThreadPool
線程池(ThreadPool)是一種相對較簡單的方法,它適應於一些須要多個線程而又較短任務(如一些常處於阻塞狀態的線程) ,它的缺點是對建立的線程不能加以控制,也不能設置其優先級。因爲每一個進程只有一個線程池,固然每一個應用程序域也只有一個線程池(對線),因此你將發現ThreadPool類的成員函數都爲static! 當你首次調用ThreadPool.QueueUserWorkItem、ThreadPool.RegisterWaitForSingleObject等,便會建立線程池實例。下面就線程池當中的兩函數做一介紹:
public static bool QueueUserWorkItem( //調用成功則返回true
WaitCallback callBack,//要建立的線程調用的委託
object state //傳遞給委託的參數
)//它的另外一個重載函數相似,只是委託不帶參數而已
此函數的做用是把要建立的線程排隊到線程池,當線程池的可用線程數不爲零時(線程池有建立線程數的限制,缺身值爲25),便建立此線程,不然就排隊到線程池等到它有可用的線程時才建立。
public static RegisteredWaitHandle RegisterWaitForSingleObject(
WaitHandle waitObject,// 要註冊的 WaitHandle
WaitOrTimerCallback callBack,// 線程調用的委託
object state,//傳遞給委託的參數
int TimeOut,//超時,單位爲毫秒,
bool executeOnlyOnce file://是否只執行一次
);
public delegate void WaitOrTimerCallback(
object state,//也即傳遞給委託的參數
bool timedOut//true表示因爲超時調用,反之則由於waitObject
);
此函數的做用是建立一個等待線程,一旦調用此函數便建立此線程,在參數waitObject變爲終止狀態或所設定的時間TimeOut到了以前,它都處於「阻塞」狀態,值得注意的一點是此「阻塞」與Thread的WaitSleepJoin狀態有很大的不一樣:當某Thread處於WaitSleepJoin狀態時CPU會按期的喚醒它以輪詢更新狀態信息,而後再次進入WaitSleepJoin狀態,線程的切換但是很費資源的;而用此函數建立的線程則不一樣,在觸發它運行以前,CPU不會切換到此線程,它既不佔用CPU的時間又不浪費線程切換時間,但CPU又如何知道什麼時候運行它?實際上線程池會生成一些輔助線程用來監視這些觸發條件,一旦達到條件便啓動相應的線程,固然這些輔助線程自己也佔用時間,可是若是你需建立較多的等待線程時,使用線程池的優點就越加明顯。見下例:
static AutoResetEvent ev=new AutoResetEvent(false);
public static int Main(string[] args)
{ ThreadPool.RegisterWaitForSingleObject(
ev,
new WaitOrTimerCallback(WaitThreadFunc),
4,
2000,
false//表示每次完成等待操做後都重置計時器,直到註銷等待
);
ThreadPool.QueueUserWorkItem (new WaitCallback (ThreadFunc),8);
Thread.Sleep (10000);
return 0;
}
public static void ThreadFunc(object b)
{ Console.WriteLine ("the object is {0}",b);
for(int i=0;i<2;i++)
{ Thread.Sleep (1000);
ev.Set();
}
}
public static void WaitThreadFunc(object b,bool t)
{ Console.WriteLine ("the object is {0},t is {1}",b,t);
}
其運行結果爲:
the object is 8
the object is 4,t is False
the object is 4,t is False
the object is 4,t is True
the object is 4,t is True
the object is 4,t is True
從以上結果咱們能夠看出線程ThreadFunc運行了1次,而WaitThreadFunc運行了5次。咱們能夠從WaitOrTimerCallback中的bool t參數判斷啓動此線程的緣由:t爲false,則表示因爲waitObject,不然則是因爲超時。另外咱們也能夠經過object b向線程傳遞一些參數。
三、Timer 它適用於需週期性調用的方法,它不在建立計時器的線程中運行,它在由系統自動分配的單獨線程中運行。這和Win32中的SetTimer方法相似。它的構造爲:public Timer( TimerCallback callback,//所需調用的方法 object state,//傳遞給callback的參數 int dueTime,//多久後開始調用callback int period//調用此方法的時間間隔); // 若是 dueTime 爲0,則 callback 當即執行它的首次調用。若是 dueTime 爲 Infinite,則 callback 不調用它的方法。計時器被禁用,但使用 Change 方法能夠從新啓用它。若是 period 爲0或 Infinite,而且 dueTime 不爲 Infinite,則 callback 調用它的方法一次。計時器的按期行爲被禁用,但使用 Change 方法能夠從新啓用它。若是 period 爲零 (0) 或 Infinite,而且 dueTime 不爲 Infinite,則 callback 調用它的方法一次。計時器的按期行爲被禁用,但使用 Change 方法能夠從新啓用它。 在建立計時器以後若想改變它的period和dueTime,咱們能夠經過調用Timer的Change方法來改變:public bool Change( int dueTime, int period);//顯然所改變的兩個參數對應於Timer中的兩參數public static int Main(string[] args){ Console.WriteLine ("period is 1000");Timer tm=new Timer (new TimerCallback (TimerCall),3,1000,1000);Thread.Sleep (2000);Console.WriteLine ("period is 500");tm.Change (0,800);Thread.Sleep (3000);return 0; }public static void TimerCall(object b){ Console.WriteLine ("timercallback; b is {0}",b);}其運行結果爲:period is 1000timercallback;b is 3 timercallback;b is 3 period is 500timercallback;b is 3 timercallback;b is 3 timercallback;b is 3 timercallback;b is 3 總結 從以上的簡單介紹,咱們能夠看出它們各自使用的場合:Thread適用於那些需對線程進行復雜控制的場合;ThreadPool適應於一些須要多個線程而又較短任務(如一些常處於阻塞狀態的線程);Timer則適用於那些需週期性調用的方法。只要咱們瞭解了它們的使用特色,咱們就能夠很好的選擇合適的方法。