C#基礎系列:多執行緒的常見用法詳解
前言:此篇就主要從博主使用過的幾種多執行緒的用法從應用層面大概介紹下。文中觀點都是博主個人的理解,如果有不對的地方望大家指正~~
1、多執行緒:使用多個處理控制代碼同時對多個任務進行控制處理的一種技術。據博主的理解,多執行緒就是該應用的主執行緒任命其他多個執行緒去協助它完成需要的功能,並且主執行緒和協助執行緒是完全獨立進行的。不知道這樣說好不好理解,後面慢慢在使用中會有更加詳細的講解。
2、多執行緒的使用:
(1)最簡單、最原始的使用方法:Thread oGetArgThread = new Thread(new ThreadStart(() =>{});這種用法應該大多數人都使用過,引數為一個ThreadStart型別的委託。將ThreadStart轉到定義可知:
1 | publicdelegatevoidThreadStart(); |
它是一個沒有引數,沒有返回值的委託。所以他的使用如下:
C#12345678910111213141516171819 | staticvoidMain(string[]args){ Thread oGetArgThread=newThread(newThreadStart(Test));oGetArgThread.IsBackground=true;oGetArgThread.Start(); for(vari=0;i<1000000;i++) { Console.WriteLine("主執行緒計數"+i); //Thread.Sleep(100); }}privatestatic |
定義一個沒有引數沒有返回值的方法傳入該委託。當然也可以不定義方法寫成匿名方法:
C#12345678910111213 | staticvoidMain(string[]args){Thread oGetArgThread=newThread(newSystem.Threading.ThreadStart(()=>{for(vari=0;i<1000000;i++){Console.WriteLine("後臺執行緒計數"+i);//Thread.Sleep(100);}}));oGetArgThread.IsBackground=true;oGetArgThread.Start();} |
這個和上面的意義相同。得到的結果如下:
說明主執行緒和後臺執行緒是互相獨立的。由系統排程資源去執行。
如果這樣那有人就要問了,如果我需要多執行緒執行的方法有引數或者有返回值或者既有引數又有返回值呢。。。彆著急我們來看看new Thread()的幾個建構函式:
C#1234 | publicThread(ParameterizedThreadStart start);publicThread(ThreadStart start);publicThread(ParameterizedThreadStart start,intmaxStackSize);publicThread(ThreadStart start,intmaxStackSize); |
轉到定義可知引數有兩類,一類是無參無返回值的委託,另一類是有參無返回值的委託。對於有引數的委託使用方法:
C#1234567891011121314 | staticvoidMain(string[]args){Thread oThread=newThread(newParameterizedThreadStart(Test2));oThread.IsBackground=true;oThread.Start(1000);}privatestaticvoidTest2(objectCount){for(vari=0;i<(int)Count;i++){Console.WriteLine("後臺執行緒計數"+i);//Thread.Sleep(100);}} |
對於有參又有返回值的委託,很顯然使用new Thread()這種方式是沒有解決方案的。其實對於有參又有返回值的委託可以使用非同步來實現:
C#123456789 | publicdelegatestringMethodCaller(stringname);//定義個代理 MethodCaller mc=newMethodCaller(GetName);stringname="my name";//輸入引數 IAsyncResult result=mc.BeginInvoke(name,null,null);stringmyname=mc.EndInvoke(result);//用於接收返回值 publicstringGetName(stringname)// 函式{returnname;} |
關於這種方式還有幾點值得一說的是:
①Thread oGetArgThread = new Thread(new ThreadStart(Test));
oGetArgThread.Join();//主執行緒阻塞,等待分支執行緒執行結束,這一步看功能需求進行選擇,主要為了多個程序達到同步的效果
②執行緒的優先順序可以通過Thread物件的Priority屬性來設定,Priority屬性對應一個列舉:
C#1234567891011121314151617181920212223 | publicenumThreadPriority{// 摘要: // 可以將 System.Threading.Thread 安排在具有任何其他優先順序的執行緒之後。Lowest=0,//// 摘要: // 可以將 System.Threading.Thread 安排在具有 Normal 優先順序的執行緒之後,在具有 Lowest 優先順序的執行緒之前。BelowNormal=1,//// 摘要: // 可以將 System.Threading.Thread 安排在具有 AboveNormal 優先順序的執行緒之後,在具有 BelowNormal 優先順序的執行緒之前。// 預設情況下,執行緒具有 Normal 優先順序。Normal=2,//// 摘要: // 可以將 System.Threading.Thread 安排在具有 Highest 優先順序的執行緒之後,在具有 Normal 優先順序的執行緒之前。AboveNormal=3,//// 摘要: // 可以將 System.Threading.Thread 安排在具有任何其他優先順序的執行緒之前。Highest=4,} |
從0到4,優先順序由低到高。
③關於多個執行緒同時使用一個物件或資源的情況,也就是執行緒的資源共享,為了避免資料紊亂,一般採用.Net悲觀鎖lock的方式處理。
C#123456789101112 | privatestaticobjectoLock=newobject();privatestaticvoidTest2(objectCount){lock(oLock){for(vari=0;i<(int)Count;i++){Console.WriteLine("後臺執行緒計數"+i);//Thread.Sleep(100);}}} |
(2)Task方式使用多執行緒:這種方式一般用在需要迴圈處理某項業務並且需要得到處理後的結果。使用程式碼如下:
C#1234567891011121314151617 | List<Task>lstTaskBD=newList<Task>();foreach(varbd inlstBoards){varbdTmp=bd;//這裡必須要用一個臨時變數varoTask=Task.Factory.StartNew(()=>{varstrCpBdCmd="rm -Rf "+bdTmp.Path+"/*;cp -R "+CombineFTPPaths(FTP_EMULATION_BD_ROOT,"bd_correct")+"/* "+bdTmp.Path+"/";oPlink.Run(bdTmp.EmulationServer.BigIP,bdTmp.EmulationServer.UserName,bdTmp.EmulationServer.Password,strCpBdCmd);Thread.Sleep(500);});lstTaskBD.Add(oTask);}Task.WaitAll(lstTaskBD.ToArray());//等待所有執行緒只都行完畢 |
使用這種方式的時候需要注意這一句 var bdTmp = bd;這裡必須要用一個臨時變數,要不然多個bd物件容易串資料。如果有興趣可以除錯看看。這種方法比較簡單,就不多說了。當然Task物件的用法肯定遠不止如此,還涉及到任務的排程等複雜的邏輯。博主對這些東西理解有限,就不講解了。
(3)執行緒池的用法:一般由於考慮到伺服器的效能等問題,保證一個時間段內系統執行緒數量在一定的範圍,需要使用執行緒池的概念。大概用法如下:
C#
12345678910111213141516171819 |