建立線程的一種簡單方式是定義一個委託,並異步調用它。 委託是方法的類型安全的引用。Delegate類 還支持異步地調用方法。在後臺,Delegate類會建立一個執行任務的線程。編程
using System; using System.Collections.Generic; using System.Linq; using System.Net.NetworkInformation; using System.Text; using System.Threading; using System.Threading.Tasks; namespace _016_線程_委託方式發起線程 { class Program { //通常咱們會爲比較耗時的操做 開啓單獨的線程去執行,好比下載操做 static int Test(int i, string str) { Console.WriteLine("test:" + i + str); Thread.Sleep(100);//讓當親線程休眠(暫停線程的執行) 單位ms return 100; } static void Main(string[] args) {//在main線程中執行 一個線程裏面語句的執行 是從上到下的 // 經過委託 開啓一個線程 Func<int, string, int> a = Test; // 開啓一個新的線程去執行 a所引用的方法 IAsyncResult ar = a.BeginInvoke(100, " mytest", null, null); // IAsyncResult 能夠取得當前線程的狀態 Console.WriteLine("main"); while (ar.IsCompleted == false)//若是當前線程沒有執行完畢 { Console.Write("."); Thread.Sleep(10); //控制子線程的檢測頻率 } int res = a.EndInvoke(ar);//取得異步線程的返回值 Console.WriteLine(res); } } }
輸出結果:安全
上面是經過循環檢測判斷線程是否結束。當咱們經過BeginInvoke開啓一個異步委託的時候,返回的結果是IAsyncResult,咱們能夠經過它的AsyncWaitHandle屬性訪問等待句柄。這個屬性返回一個WaitHandler類型的對象,它中的WaitOne()方法能夠等待委託線程完成其任務,WaitOne方法能夠設置一個超時時間做爲參數(要等待的最長時間),若是發生超時就返回false。多線程
using System; using System.Collections.Generic; using System.Linq; using System.Net.NetworkInformation; using System.Text; using System.Threading; using System.Threading.Tasks; namespace _016_線程_委託方式發起線程 { class Program { //通常咱們會爲比較耗時的操做 開啓單獨的線程去執行,好比下載操做 static int Test(int i, string str) { Console.WriteLine("test:" + i + str); Thread.Sleep(100);//讓當親線程休眠(暫停線程的執行) 單位ms return 100; } static void Main(string[] args) {//在main線程中執行 一個線程裏面語句的執行 是從上到下的 // 經過委託 開啓一個線程 Func<int, string, int> a = Test; IAsyncResult ar = a.BeginInvoke(100, " mytest", null, null);// 開啓一個新的線程去執行 a所引用的方法 // IAsyncResult 能夠取得當前線程的狀態 Console.WriteLine("main"); //檢測線程結束 bool isEnd = ar.AsyncWaitHandle.WaitOne(1000);//1000毫秒錶示超時時間,若是等待了1000毫秒 線程尚未結束的話 那麼這個方法會返回false 若是在1000毫秒之內線程結束了,那麼這個方法會返回true if (isEnd) { int res = a.EndInvoke(ar); Console.WriteLine(res); } } } }
等待委託的結果的第3種方式是使用異步回調。在BeginInvoke的第三個參數中,能夠傳遞一個知足AsyncCallback委託的方法,AsyncCallback委託定義了一個IAsyncResult類型的參數其返回類型是void。對於最後一個參數,能夠傳遞任意對象,以便從回調方法中訪問它。異步
using System; using System.Collections.Generic; using System.Linq; using System.Net.NetworkInformation; using System.Text; using System.Threading; using System.Threading.Tasks; namespace _016_線程_委託方式發起線程 { class Program { //通常咱們會爲比較耗時的操做 開啓單獨的線程去執行,好比下載操做 static int Test(int i, string str) { Console.WriteLine("test:" + i + str); Thread.Sleep(100);//讓當親線程休眠(暫停線程的執行) 單位ms return 100; } static void Main(string[] args) {//在main線程中執行 一個線程裏面語句的執行 是從上到下的 Console.WriteLine("main"); //經過回調 檢測線程結束 Func<int, string, int> a = Test; // 倒數第二個參數是一個委託類型的參數,表示回調函數,就是當線程結束的時候會調用這個委託指向的方法 倒數第一個參數用來給回調函數傳遞數據 IAsyncResult ar = a.BeginInvoke(100, " mytest", OnCallBack, a); Console.ReadKey(); } static void OnCallBack( IAsyncResult ar ) { Func<int, string, int> a = ar.AsyncState as Func<int, string, int>; int res = a.EndInvoke(ar); Console.WriteLine(res+"在回調函數中取得結果"); } } }
在第三種方法中,咱們可使用Lamba表達式異步編程
using System; using System.Collections.Generic; using System.Linq; using System.Net.NetworkInformation; using System.Text; using System.Threading; using System.Threading.Tasks; namespace _016_線程_委託方式發起線程 { class Program { //通常咱們會爲比較耗時的操做 開啓單獨的線程去執行,好比下載操做 static int Test(int i, string str) { Console.WriteLine("test:" + i + str); Thread.Sleep(100);//讓當親線程休眠(暫停線程的執行) 單位ms return 100; } static void Main(string[] args) { Console.WriteLine("main"); //經過回調 檢測線程結束 Func<int, string, int> a = Test; a.BeginInvoke(100, "siki", ar => { int res = a.EndInvoke(ar); Console.WriteLine(res + "在lambda表達式中取得"); }, null); Console.ReadKey(); } } }
咱們能夠經過Thread類來建立線程,咱們構造一個thread對象的時候,能夠傳遞一個靜態方法,也能夠傳遞一個對象的普通方法。咱們先傳遞一個靜態方法:函數
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; namespace _017_線程_經過Thread發起線程 { class Program { // 注意:線程調用的方法定義中,參數要用object static void DownloadFile(object filename) { // Thread.CurrentThread.ManagedThreadId表示當前線程ID Console.WriteLine("開始下載:" +Thread.CurrentThread.ManagedThreadId +filename); Thread.Sleep(2000); Console.WriteLine("下載完成"); } static void Main(string[] args) { //建立線程,傳入要執行的方法 Thread t = new Thread(DownloadFile); // 開啓線程,若是線程調用的方法有參數,在Start中傳入 t.Start("test"); Console.WriteLine("Main"); Console.ReadKey(); // 使用Lamba表達式方法 //Thread t = new Thread(() => //{ // Console.WriteLine("開始下載:" + Thread.CurrentThread.ManagedThreadId); // Thread.Sleep(2000); // Console.WriteLine("下載完成"); //}); //t.Start(); } } }
輸出結果:this
下面咱們傳遞一個對象的普通方法,先定義一個類:spa
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; namespace _017_線程_經過Thread發起線程 { class MyThread { private string filename; private string filepath; public MyThread(string fileName, string filePath) { this.filename = fileName; this.filepath = filePath; } public void DownFile() { Console.WriteLine("開始下載"+filepath+filename); Thread.Sleep(2000); Console.WriteLine("下載完成"); } } }
而後建立線程:線程
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; namespace _017_線程_經過Thread發起線程 { class Program { static void Main(string[] args) { MyThread my = new MyThread("xxx.bt","http://www.xxx.bbs"); Thread t = new Thread(my.DownFile); t.Start(); } } }
建立線程須要時間。 若是有不一樣的小任務要完成,就能夠事先建立許多線程 , 在應完成這些任務時發出請求。 這個線程數最好在須要更多的線程時增長,在須要釋放資源時減小。3d
不須要 本身建立線程池,系統已經有一個ThreadPool類管理線程。 這個類會在須要時增減池中線程的線程數,直到達到最大的線程數。 池中的最大線程數是可配置的。 在雙核 CPU中 ,默認設置爲1023個工做線程和 1000個 I/o線程。也能夠指定在建立線程池時應當即啓動的最小線程數,以及線程池中可用的最大線程數。 若是有更多的做業要處理,線程池中線程的個數也到了極限,最新的做業就要排隊,且必須等待線程完成其任務。
使用線程池須要注意的事項:
線程池中的全部線程都是後臺線程 。 若是進程的全部前臺線程都結束了,全部的後臺線程就會中止。 不能把入池的線程改成前臺線程 。
不能給入池的線程設置優先級或名稱。
入池的線程只能用於時間較短的任務。 若是線程要一直運行(如 Word的拼寫檢查器線程),就應使用Thread類建立一個線程。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; namespace _018_線程_線程池 { class Program { // 注意:使用線程池添加的執行函數,必需要有一個object類型的參數,即時不用 static void ThreadMethod(object state) { Console.WriteLine("線程開始:"+Thread.CurrentThread.ManagedThreadId); Thread.Sleep(2000); Console.WriteLine("線程結束"); } static void Main(string[] args) { //開啓一個工做線程 ThreadPool.QueueUserWorkItem(ThreadMethod); ThreadPool.QueueUserWorkItem(ThreadMethod); ThreadPool.QueueUserWorkItem(ThreadMethod); ThreadPool.QueueUserWorkItem(ThreadMethod); ThreadPool.QueueUserWorkItem(ThreadMethod); ThreadPool.QueueUserWorkItem(ThreadMethod); ThreadPool.QueueUserWorkItem(ThreadMethod); Console.ReadKey(); } } }
輸出結果:
在.NET4 新的命名空間System.Threading.Tasks包含了類抽象出了線程功能,在後臺使用的ThreadPool進行管理的。任務表示應完成某個單元的工做。這個工做能夠在單獨的線程中運行,也能夠以同步方式啓動一個任務。 任務也是異步編程中的一種實現方式。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; namespace _019_線程_任務 { class Program { static void ThreadMethod() { Console.WriteLine("任務開始"); Thread.Sleep(2000); Console.WriteLine("任務結束"); } static void Main(string[] args) { // 經過Task建立 Task t = new Task(ThreadMethod); t.Start(); // 經過任務工廠建立 //TaskFactory tf = new TaskFactory(); //Task t = tf.StartNew(ThreadMethod); Console.WriteLine("Main"); Console.ReadKey(); } } }
輸出結果:
連續任務:若是一個任務t1的執行是依賴於另外一個任務t2的,那麼就須要在這個任務t2執行完畢後纔開始執行t1。這個時候咱們可使用連續任務。
static void DoFirst(){ Console.WriteLine("do in task : "+Task.CurrentId); Thread.Sleep(3000); } static void DoSecond(Task t){ Console.WriteLine("task "+t.Id+" finished."); Console.WriteLine("this task id is "+Task.CurrentId); Thread.Sleep(3000); } Task t1 = new Task(DoFirst); Task t2 = t1.ContinueWith(DoSecond); Task t3 = t1.ContinueWith(DoSecond); Task t4 = t2.ContinueWith(DoSecond);
任務層次結構:咱們在一個任務中啓動一個新的任務,至關於新的任務是當前任務的子任務,兩個任務異步執行,若是父任務執行完了可是子任務沒有執行完,它的狀態會設置爲WaitingForChildrenToComplete,只有子任務也執行完了,父任務的狀態就變成RunToCompletion
static void Main(){ var parent = new Task(ParentTask); parent.Start(); Thread.Sleep(2000); Console.WriteLine(parent.Status); Thread.Sleep(4000); Console.WriteLine(parent.Status); Console.ReadKey(); } static void ParentTask(){ Console.WriteLine("task id "+Task.CurrentId); var child = new Task(ChildTask); child.Start(); Thread.Sleep(1000); Console.WriteLine("parent started child , parent end"); } static void ChildTask(){ Console.WriteLine("child"); Thread.Sleep(5000); Console.WriteLine("child finished ");