簡單理解
Thread:是一個指令序列,個體對象。
Threadpool:在使用Thread的過程中,程序員要為每個希望并發(fā)的序列new一個線程,很麻煩,因此希望有一個統(tǒng)一管理線程的方法,程序員就不需要關(guān)注線程的申請管理問題,所以就對Thread進(jìn)行一系列封裝,有了ThreadPool。使用Threadpool,把需要并發(fā)的序列添加進(jìn)線程池,線程池根據(jù)其線程列表中的線程的空閑情況,動態(tài)為并發(fā)序列申請線程。
Task:再后來,程序員發(fā)現(xiàn)在使用Threadpool的過程當(dāng)中還是存在很多不便,比如:(1)ThreadPool不支持線程的取消、完成、失敗通知等交互性操作;(2)ThreadPool不支持線程執(zhí)行的先后次序;那么怎么辦,那就繼續(xù)對ThreadPool進(jìn)行封裝唄,于是.net Framework4.0有了TPL和Task。
Thread
選用Thread的一個重要原因之一是因?yàn)樗腡hread.Sleep()成員,因?yàn)樗赥ask或ThreadPool中都沒有等價(jià)物。不過如果不會帶來太多沒必要的復(fù)雜性,可以考慮用一個計(jì)時器代替Sleep()。
對于Thread這個知識點(diǎn),重點(diǎn)在于同步、死鎖和競態(tài)等問題,感覺關(guān)鍵點(diǎn)在于對lock()和WaitHandler的理解。這里,介紹下參數(shù)的傳遞和獲取。
下面這部分主要查看MSDN的文檔:http://msdn.microsoft.com/en-us/library/wkays279.aspx
因?yàn)門hread的構(gòu)造函數(shù)需要傳遞一個無返回類型和無參的委托引用,因此最理想的辦法是把需要傳遞給線程的方法封裝在一個類里面,然后將需要傳遞給這個線程的參數(shù)定義為該類的字段。比如,要把下面這個帶參函數(shù)通過線程調(diào)用:
1 public double CalArea(double length,double width)2 {3 double Area=length*width;4 return Area;5 }
可使用如下方法進(jìn)行:
1 public class AreaClass 2 { 3 public double width; 4 public double length; 5 public double area; 6 public void CalArea() 7 { 8 area=width*length; 9 }10 }11 12 public void TestAreaCal()13 {14 AreaClass areaCal=new AreaClass();15 System.Threading.Thread thread1=new System.Threading.Thread(areaCal.CalArea);16 areaCal.width=2;17 areaCal.length=3;18 thread1.start();19 }但要注意,如果在執(zhí)行完thread1.start()之后,立刻查看areaCal.area的值,并不一定就存在了。要獲得有效的返回值得簡單方法將在下面介紹。
簡單方法是使用BackgroundWorker類。利用BackgroundWorker類管理你的線程,并在線程執(zhí)行完畢后產(chǎn)生一個事件,然后在對應(yīng)的事件處理函數(shù)中處理結(jié)果。
1 class AreaClass2 2 { 3 public double Base; 4 public double Height; 5 public double CalcArea() 6 { 7 // Calculate the area of a triangle. 8 return 0.5 * Base * Height; 9 }10 }11 12 PRivate System.ComponentModel.BackgroundWorker BackgroundWorker113 = new System.ComponentModel.BackgroundWorker();14 15 private void TestArea2()16 {17 InitializeBackgroundWorker();18 19 AreaClass2 AreaObject2 = new AreaClass2();20 AreaObject2.Base = 30;21 AreaObject2.Height = 40;22 23 // Start the asynchronous Operation.24 BackgroundWorker1.RunWorkerAsync(AreaObject2);25 }26 27 private void InitializeBackgroundWorker()28 {29 // Attach event handlers to the BackgroundWorker object.30 BackgroundWorker1.DoWork +=31 new System.ComponentModel.DoWorkEventHandler(BackgroundWorker1_DoWork);32 BackgroundWorker1.RunWorkerCompleted +=33 new System.ComponentModel.RunWorkerCompletedEventHandler(BackgroundWorker1_RunWorkerCompleted);34 }35 36 private void BackgroundWorker1_DoWork(37 object sender,38 System.ComponentModel.DoWorkEventArgs e)39 {40 AreaClass2 AreaObject2 = (AreaClass2)e.Argument;41 // Return the value through the Result property.42 e.Result = AreaObject2.CalcArea();43 }44 45 private void BackgroundWorker1_RunWorkerCompleted(46 object sender,47 System.ComponentModel.RunWorkerCompletedEventArgs e)48 {49 // access the result through the Result property. 50 double Area = (double)e.Result;51 MessageBox.Show("The area is: " + Area.ToString());52 }ThreadPool
ThreadPool是一個靜態(tài)類。
管理過程:
許多程序生成的線程有一大半時間處在休眠狀態(tài),直到產(chǎn)生一個事件喚醒它們,另一部分線程只會被周期性地喚醒以更改狀態(tài)信息。ThreadPool可以通過為你的應(yīng)用程序提供由系統(tǒng)管理的線程來提高應(yīng)用程序?qū)€程的使用率。線程池中會有一個線程監(jiān)看線程池隊(duì)列中的等待操作,當(dāng)線程池中的線程完成它的任務(wù)后,它就會回到等待線程的隊(duì)列當(dāng)中,等待重新使用。如果線程池中的所有線程都在運(yùn)行,那么新的任務(wù)會加進(jìn)等待隊(duì)列中。對線程的重復(fù)使用降低了為每一個任務(wù)都開啟一個新線程的造成的程序開銷。
特點(diǎn):
ThreadPool里面的線程管理是由類庫(或操作系統(tǒng))完成的,而不是由應(yīng)用程序進(jìn)行管理。
線程池里面的線程都是后臺線程(即它們的isBackgroud屬性為true)。也就意味著當(dāng)所有前臺線程都運(yùn)行完畢后,線程池的線程不會保持程序的運(yùn)行。
線程池一般使用在服務(wù)應(yīng)用程序上。每個進(jìn)來的請求都分配給線程池中的每一個線程,這樣就可以異步處理各個請求。
使用:
線程池的簡單使用可通過QueueUserWorkItem方法進(jìn)行。如果需要向任務(wù)中傳遞參數(shù),可以通過傳遞一個state object來進(jìn)行,如果有多個參數(shù)要傳遞,可將多個參數(shù)封裝在結(jié)構(gòu)或?qū)ο蟮淖侄萎?dāng)中。但要注意,一旦一個方法加入了線程隊(duì)列,就無法取消該方法的執(zhí)行:
public static void Main(){System.Threading.ThreadPool.QueueUserWorkItem(new System.Threading.WaitCallback(ASynTask),ASynData);//other code snippets......}public void ASynTask(object state){//codes need to executed asynchronously}線程池的大小可以通過GetMaxThreads或SetMaxThreads方法訪問或設(shè)置,但注意過大或過小都會影響程序的性能。
另外,獲取ThreadPool返回值有兩種方法,具體查看上面Thread所講的辦法。
Task和TPL
關(guān)于Task和TPL的知識還是以后再詳細(xì)介紹。這里簡單講下Task的主要特征。
1 public static void Main() 2 { 3 CancellationTokenSource cancellationTokenSource=new CancellationTokenSource(); 4 Task task.Task.Factory.StartNew(()=>LoopWork(cancellationTokenSource.Token),cancellationTokenSource.Token); 5 Thread.sleep(1000); 6 cancellationTokenSource.Cancel(); 7 task.wait(); 8 } 9 10 private static void LoopWork(CancellationToken cancellationToken)11 {12 while(!cancellationToken.IsCancellationRequested)13 {14 //work need to be done15 }16 }Task還支持IDisposable方法,這是支持Wait()功能所需要的。
并行迭代
在使用for循環(huán)迭代時,處理器會按順序依次迭代。但如果每次迭代之間互不影響,而且如果系統(tǒng)有多個處理器,我們可以讓每個處理器都負(fù)責(zé)一個迭代,從而提高效率。
相關(guān)類:System.Threading.Tasks.Parallel。
相關(guān)方法:Parallel.For()和Parallel.Foreach()
注意三點(diǎn):計(jì)算機(jī)具有多個處理器,并行迭代的順序不是固定的,在使用時還要同時考慮迭代內(nèi)部代碼的非原子性帶來的競態(tài)問題。
1 //將10000個小寫單詞變成大寫 2 static void Main() 3 { 4 int iterations=10000; 5 string[] Word=new string[iterations]; 6 //此處初始化word,輸入10000個小寫單詞 7 Parallel.For(0,iterations,(i)=> 8 { 9 word[i]=word[i].toUpper();10 });11 } 新聞熱點(diǎn)
疑難解答
圖片精選