使用線程有幾個緣由。假設從應用程序中進行網絡調用須要必定的時間。咱們不但願用戶界面中止響應。讓用戶一直等待從服務器返回一個響應。用戶同時執行其餘一些操做,過着甚至取消發送給服務器的請求。這些均可以使用線程來實現。
對於全部的須要等待操做,列如,由於文件,數據庫或網絡訪問都須要必定的時間,此時就能夠啓動一個新線程,同時完成其餘任務。即便是處理密集型的任務,線程也是有幫助的。 一個進程的多線程能夠同時在不一樣CPU上運行,或多核CPU的不一樣內核上。
若是多線程同時運行,訪問相同的數據,就很容易出現問題,必須實現同步機制。
下面介紹的主要命名空間是:Syetem.Threading和System.Threading.Tasks數據庫
線程是程序中獨立的指令流。,
運行在服務器上的應用程序中,等待客戶請求的線程,稱爲偵聽器線程。 只要接受到請求,就把它傳遞給另外一個工做線程,以後繼續與客戶同行。偵聽器線程會當即返回,接受下一個客戶發送的下一個請求。
進程包含資源,每一個進程都分配了虛擬內存。一個進程至少包含一個線程。操做系統會調用線程。線程有一個優先級,實際上正在處理的程序的位置計數器,一個存儲器局部變量的棧。每一個線程都有本身的棧,但程序代碼的內存和堆由一個進程的全部線程共享。 這使一個進程的全部線程之間的通訊很是快–該進程的全部線程都尋址相同的虛擬內存。可是,這也使處理比較困難,由於多線程能夠修改同一個內存位置。
線程是運行程序所必須的。在.NET4.0以前,必須直接使用Thread類和ThreadPool類。如今,.NET對着兩個類作了抽象,容許使用Parallel類和Task類。
爲編寫可以利用並行性的代碼,必須區分兩種主要的場景:任務並行性和數據並行性。數組
Parallel類是對線程的一個很好的抽象。該類位於System.Threading.Tasks命名空間中,提供了數據和任務並行性
Parallel類定義了並行的for和foreach的靜態方法。對於C#的for和foreach語句而言,循環從一個線程中運行。Parallel類使用多個任務,所以使用多個線程來完成做業。
Parallel.For()和Parallel.ForEach()方法在每次迭代中調用相同的代碼,而Parallel.Invoke()方法容許同時調用不一樣的方法。Parallel.Invoke用於任務並行性,而Parallel.ForEach用於數據並行性。服務器
ParallelLoopResult result = Parallel.For(0, 10, i => { Console.WriteLine("{0},task:{1},thread:{2}", i , Task.CurrentId, Thread.CurrentThread.ManagedThreadId); Thread.Sleep(10); // Task.Delay(100);//是一個異步方法,用於釋放線程供其餘任務使用。 }); Console.WriteLine("IS complete:{0}", result.IsCompleted); Console.ReadKey();
程序的執行依次是:0-2-3-1-4-6-8-7-5-9;共有五個任務和五個線程。任務不必定映射到一個線程上,線程也能夠被不一樣的任務重用。markdown
在4.5中新增的Thread.Sleep方法,而不是Task.Delay方法。Task.Delay是一個異步方法,用於釋放線程提供其餘任務使用。下面代碼使用await關鍵字,因此一旦延遲就會調用。網絡
ParallelLoopResult asyncResult = Parallel.For(0, 10, async i => { Console.WriteLine("{0},task:{1},thread:{2}", i , Task.CurrentId, Thread.CurrentThread.ManagedThreadId); //使用await關鍵字,因此一旦完成延遲,就會當即調用這些代碼 // 延遲後執行的代碼和延遲前執行的代碼能夠運行不用的線程中 await Task.Delay(100); Console.WriteLine("{0},task:{1},thread:{2}", i , Task.CurrentId, Thread.CurrentThread.ManagedThreadId); }); Console.WriteLine("IS complete:{0}", asyncResult.IsCompleted); Console.ReadKey();
在代用Thread.Delay方法後,線程發生了變化。在輸出中還能夠看到,任務再也不存在,只有線程留下了,並且這裏重用了前面的線程。另外一重要方面,Parallel類的For方法並無等待延遲,而是直接完成。Parallel類只等待它建立的任務,而不是等待其餘後臺活動。在延遲後,也有可能徹底看不到方法的輸出,出現這種狀況的緣由是主線程(前臺線程)結束,全部的後臺線程被終止。多線程
也能夠提早中斷Parallel.For()方法嗎,而不是完成全部迭代。For()方法有一個重載接受Action異步
ParallelLoopResult asyncResult = Parallel.For(10, 40, async (int i,ParallelLoopState pls ) => { Console.WriteLine("{0},task:{1},thread:{2}", i , Task.CurrentId, Thread.CurrentThread.ManagedThreadId); //使用await關鍵字,因此一旦完成延遲,就會當即調用這些代碼 // 延遲後執行的代碼和延遲前執行的代碼能夠運行不用的線程中 await Task.Delay(10); if (i > 10) pls.Break(); //Console.WriteLine("{0},task:{1},thread:{2}", i // , Task.CurrentId, Thread.CurrentThread.ManagedThreadId); }); Console.WriteLine("IS complete:{0}", asyncResult.IsCompleted); Console.WriteLine("lowest break iteration :{0}", asyncResult.LowestBreakIteration); Console.ReadKey();
Parallel.ForEach()方法遍歷實現了IEnumerable的集合,其方式相似於foreach語句,但以異步方法遍歷。async
string[] data = { "aaa", "bbb", "ccc", "ddd", "eee", "fff", "asss" }; ParallelLoopResult resule = Parallel.ForEach<string>(data, s => { Console.WriteLine(s); });
若是須要中斷循環,使用ForEach()方法重載和ParallelLoopState 參數。
ForEach()方法重載:oop
public static ParallelLoopResult ForEach<TSource>(IEnumerable<TSource> source, Action<TSource, ParallelLoopState, long> body)
實現代碼以下:post
string[] data = { "aaa", "bbb", "ccc", "ddd", "eee", "fff", "asss" }; ParallelLoopResult resule = Parallel.ForEach<string>(data,(s,pls,l) => { if (s.Contains("bbb")) pls.Break(); Console.WriteLine(s); Console.WriteLine( "LowestBreakIteration:" + pls.LowestBreakIteration); }); Console.ReadKey();
2.3經過Parallel.Invoke()方法調用多個方法
若是多個任務並行運行,就可使用parallel.Invoke()方法,它提供了任務並行模式。Parallel.Invoke()方法容許傳遞一個Action委託數組,在其中能夠指定運行的方法。並行調用Foo和Bar方法
static void ParallelInvoke() { Parallel.Invoke(Foo,Bar); } static void Foo() { Console.WriteLine("foo"); } static void Bar() { Console.WriteLine("Bar"); }
Parallel類使用起來十分方便,並且可使用任務,已能夠用於數據並行性。若是想要更細緻的控制,而且不想等待Parallel類結束後纔開始動做,就可使用Task類,固然,結合使用Task類和Parallel類也是能夠的