Parallel類

1.概述

使用線程有幾個緣由。假設從應用程序中進行網絡調用須要必定的時間。咱們不但願用戶界面中止響應。讓用戶一直等待從服務器返回一個響應。用戶同時執行其餘一些操做,過着甚至取消發送給服務器的請求。這些均可以使用線程來實現。
對於全部的須要等待操做,列如,由於文件,數據庫或網絡訪問都須要必定的時間,此時就能夠啓動一個新線程,同時完成其餘任務。即便是處理密集型的任務,線程也是有幫助的。 一個進程的多線程能夠同時在不一樣CPU上運行,或多核CPU的不一樣內核上。
若是多線程同時運行,訪問相同的數據,就很容易出現問題,必須實現同步機制。
下面介紹的主要命名空間是:Syetem.Threading和System.Threading.Tasks數據庫

線程是程序中獨立的指令流。
運行在服務器上的應用程序中,等待客戶請求的線程,稱爲偵聽器線程。 只要接受到請求,就把它傳遞給另外一個工做線程,以後繼續與客戶同行。偵聽器線程會當即返回,接受下一個客戶發送的下一個請求。
進程包含資源,每一個進程都分配了虛擬內存。一個進程至少包含一個線程。操做系統會調用線程。線程有一個優先級,實際上正在處理的程序的位置計數器,一個存儲器局部變量的棧。每一個線程都有本身的棧,但程序代碼的內存和堆由一個進程的全部線程共享。 這使一個進程的全部線程之間的通訊很是快–該進程的全部線程都尋址相同的虛擬內存。可是,這也使處理比較困難,由於多線程能夠修改同一個內存位置。
線程是運行程序所必須的。在.NET4.0以前,必須直接使用Thread類和ThreadPool類。如今,.NET對着兩個類作了抽象,容許使用Parallel類和Task類。
爲編寫可以利用並行性的代碼,必須區分兩種主要的場景:任務並行性和數據並行性數組

  • 任務並行性:使用CUP的代碼被並行化。CPU的多個核心會被利用起來,更加快速的完成包含多個任務的活動,而不是在一個核心中按順序一個一個地執行任務。
  • 數據並行性:使用數據集合,在集合上執行的工做被劃分爲多個任務
    兩則能夠混合起來使用。

Parallel類

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

也能夠提早中斷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();

 

這裏寫圖片描述

2.2使用Parallel.ForEach()方法循環

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類也是能夠的

相關文章
相關標籤/搜索