5天玩轉C#並行和多線程編程 —— 次日 並行集合和PLinq

  在上一篇博客 5天玩轉C#並行和多線程編程 —— 第一天 認識Parallel中,咱們學習了Parallel的用法。並行編程,本質上是多線程的編程,那麼當多個線程同時處理一個任務的時候,必然會出現資源訪問問題,及所謂的線程安全。就像現實中,咱們開發項目,就是一個並行的例子,把不一樣的模塊分給不一樣的人,同時進行,才能在短的時間內作出大的項目。若是你們都只管本身寫本身的代碼,寫完後發現合併不到一塊兒,那麼這種並行就沒有了意義。
  並行算法的出現,隨之而產生的也就有了並行集合,及線程安全集合;微軟向的也算周到,沒有忘記linq,也推出了linq的並行版本,plinq - Parallel Linq.
 
 1、並行集合 —— 線程安全集合
  並行計算使用的多個線程同時進行計算,因此要控制每一個線程對資源的訪問,咱們先來看一下平時經常使用的List<T>集合,在並行計算下的表現,新建一個控制檯應用程序,添加一個PEnumerable類(固然你也直接寫到main方法裏面測試,建議分開寫),寫以下方法:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Collections.Concurrent;

namespace ThreadPool
{
   public class PEnumerable
   {
      public static void ListWithParallel()
      {
         List<int> list = new List<int>();
         Parallel.For(0, 10000, item =>
         {
            list.Add(item);
         });
         Console.WriteLine("List's count is {0}",list.Count());
      }
   }
}

點擊F5運行,獲得以下結果:性能

看到結果中顯示的5851,可是咱們循環的是10000次啊!怎麼結果不對呢?這是由於List<T>是非線程安全集合,意思就是說全部的線程均可以修改他的值。學習

下面咱們來看下並行集合 —— 線程安全集合,在System.Collections.Concurrent命名空間中,首先來看一下ConcurrentBag<T>泛型集合,其用法和List<T>相似,先來寫個方法測試一下:測試

public static void ConcurrentBagWithPallel()
      {
         ConcurrentBag<int> list = new ConcurrentBag<int>();
         Parallel.For(0, 10000, item =>
         {
            list.Add(item);
         });
         Console.WriteLine("ConcurrentBag's count is {0}", list.Count());
      }

同時執行兩個方法,結果以下:大數據

能夠看到,ConcurrentBag集合的結果是正確的。下面咱們修改代碼看看ConcurrentBag裏面的數據究竟是怎麼存放的,修改代碼以下:

public static void ConcurrentBagWithPallel()
      {
         ConcurrentBag<int> list = new ConcurrentBag<int>();
         Parallel.For(0, 10000, item =>
         {
            list.Add(item);
         });
         Console.WriteLine("ConcurrentBag's count is {0}", list.Count());
         int n = 0;
         foreach(int i in list)
         {
            if (n > 10)
               break;
            n++;
            Console.WriteLine("Item[{0}] = {1}",n,i);
         }
         Console.WriteLine("ConcurrentBag's max item is {0}", list.Max());

      }

先來看一下運行結果:

能夠看到,ConcurrentBag中的數據並非按照順序排列的,順序是亂的,隨機的。咱們平時使用的Max、First、Last等linq方法都還有。其時分相似Enumerable的用法,你們能夠參考微軟的MSDN瞭解它的具體用法。

關於線程安全的集合還有不少,和咱們平時用的集合都差很少,好比相似Dictionary的ConcurrentDictionary,還有ConcurrentStack,ConcurrentQueue等。

 

 2、Parallel Linq的用法及性能

一、AsParallel

前面瞭解了並行的For和foreach,今天就來看一下Linq的並行版本是怎麼樣吧?爲了測試,咱們添加一個Custom類,代碼以下:

public class Custom
   {
      public string Name { get; set; }
      public int Age { get; set; }
      public string Address { get; set; }
   }

 寫以下測試代碼:

 public static void TestPLinq()
      {
         Stopwatch sw = new Stopwatch();
         List<Custom> customs = new List<Custom>();
         for (int i = 0; i < 2000000; i++)
         {
            customs.Add(new Custom() { Name = "Jack", Age = 21, Address = "NewYork" });
            customs.Add(new Custom() { Name = "Jime", Age = 26, Address = "China" });
            customs.Add(new Custom() { Name = "Tina", Age = 29, Address = "ShangHai" });
            customs.Add(new Custom() { Name = "Luo", Age = 30, Address = "Beijing" });
            customs.Add(new Custom() { Name = "Wang", Age = 60, Address = "Guangdong" });
            customs.Add(new Custom() { Name = "Feng", Age = 25, Address = "YunNan" });
         }

         sw.Start();
         var result = customs.Where<Custom>(c => c.Age > 26).ToList();
         sw.Stop();
         Console.WriteLine("Linq time is {0}.",sw.ElapsedMilliseconds);

         sw.Restart();
         sw.Start();
         var result2 = customs.AsParallel().Where<Custom>(c => c.Age > 26).ToList();
         sw.Stop();
         Console.WriteLine("Parallel Linq time is {0}.", sw.ElapsedMilliseconds);
      }

其實也就是加了一個AsParallel()方法,下面來看下運行結果:

時間相差了一倍,不過有時候不會相差這麼多,要看系統當前的資源利用率。你們能夠多測試一下。

其實,AsParallel()這個方法能夠應用與任何集合,包括List<T>集合,從而提升查詢速度和系統性能。

 

二、GroupBy方法

在項目中,咱們常常要對數據作處理,好比分組統計,咱們知道在linq中也能夠實現,今天來學習一下新的ToLookup方法,寫一個測試方法,代碼以下:

public static void OrderByTest()
      {
         Stopwatch stopWatch = new Stopwatch();
         List<Custom> customs = new List<Custom>();
         for (int i = 0; i < 2000000; i++)
         {
            customs.Add(new Custom() { Name = "Jack", Age = 21, Address = "NewYork" });
            customs.Add(new Custom() { Name = "Jime", Age = 26, Address = "China" });
            customs.Add(new Custom() { Name = "Tina", Age = 29, Address = "ShangHai" });
            customs.Add(new Custom() { Name = "Luo", Age = 30, Address = "Beijing" });
            customs.Add(new Custom() { Name = "Wang", Age = 60, Address = "Guangdong" });
            customs.Add(new Custom() { Name = "Feng", Age = 25, Address = "YunNan" });
         }

         stopWatch.Restart();
         var groupByAge = customs.GroupBy(item => item.Age).ToList();
         foreach (var item in groupByAge)
         {
            Console.WriteLine("Age={0},count = {1}", item.Key, item.Count());
         }
         stopWatch.Stop();

         Console.WriteLine("Linq group by time is: " + stopWatch.ElapsedMilliseconds);


         stopWatch.Restart();
         var lookupList = customs.ToLookup(i => i.Age);
         foreach (var item in lookupList)
         {
            Console.WriteLine("LookUP:Age={0},count = {1}", item.Key, item.Count());
         }
         stopWatch.Stop();
         Console.WriteLine("LookUp group by time is: " + stopWatch.ElapsedMilliseconds);
      }

運行結果以下:

ToLookup方法是將集合轉換成一個只讀集合,因此在大數據量分組時性能優於List.你們能夠查閱相關資料,這裏因爲篇幅問題,再也不細說。

相關文章
相關標籤/搜索