LINQ基礎(三)

来源:http://www.cnblogs.com/afei-24/archive/2017/05/16/6860753.html
-Advertisement-
Play Games

一.並行LINQ System.Linq名稱空間中包含的類ParallelEnumerable可以分解查詢的工作,使其分佈在多個線程上。 儘管Enumerable類給IEnumerable<T>介面定義了擴展方法,但ParallelEnumerable類的大多數擴展方法是ParallerQuery< ...


一.並行LINQ
  System.Linq名稱空間中包含的類ParallelEnumerable可以分解查詢的工作,使其分佈在多個線程上。
  儘管Enumerable類給IEnumerable<T>介面定義了擴展方法,但ParallelEnumerable類的大多數擴展方法是ParallerQuery<TSource>類的擴展。例如,AsParallel()方法,它擴展了IEnumerable<T>介面,返回ParallelQuery<T>類,所以正常的集合類可以以平行方式查詢。

  1.並行查詢
  下麵演示並行LINQ(Parallel LINQ,PLINQ):

//用隨機值填充一個大型的int集合
        // Enumerable.Range(0, arraySize),生成指定範圍內的整數的空序列。
        //Select(x => r.Next(140)),用小於140的數填充集合
        static IEnumerable<int> SampleData()
        {
          const int arraySize = 100000000;
          var r = new Random();
          return Enumerable.Range(0, arraySize).Select(x => r.Next(140)).ToList();
        }

        static void IntroParallel()
        {
          var data = SampleData();

          var watch = new Stopwatch();

        //非並行LINQ
          watch.Start();
          var q1 = (from x in data
                    where Math.Log(x) < 4
                    select x).Average();
          watch.Stop();
          Console.WriteLine("sync {0}, result: {1}", watch.ElapsedMilliseconds, q1);

          watch.Reset();
          
          //使用data.AsParallel()進行並行LINQ
          watch.Start();
          var q2 = (from x in data.AsParallel()
                where Math.Log(x) < 4
                select x).Average();
          watch.Stop();
          Console.WriteLine("async {0}, result: {1}", watch.ElapsedMilliseconds, q2);
        }

  輸出;
    
  發現並行查詢時間用的少,在並行查詢時CPU利用率達到100%

  與LINQ基礎(二)(http://www.cnblogs.com/afei-24/p/6845551.html)中的LINQ查詢一樣,編譯器會修改語法,以調用AsParallel,Where(),Select(),Average()方法:
  var q2 = data.AsParallel().Where(x => Math.Log(x)<4).Select(x => x).Average();
  AsParallel()方法用ParallerEnumerable類定義,以擴展IEnumerable<T>介面,所以可以對簡單的數組調用它。AsParallel()方法返回ParallerQuery<T>。因為返回的類型,所以編譯器選擇的Where()方法是ParallerEnumerable.Where(),而不是Enumerable.Where()。
  對於PrarllelEnumerable類,查詢是分區的,以便多個線程可以同時處理該查詢。集合可以分為多個部分,其中每個部分由不同的線程處理。完成分區的工作後,就需要合併,獲得所有部分的總和。

  2.分區器
  AsParallel()方法不僅擴展了IEnumerable<T>介面,還擴展了Partitioner類。通過它可以影響要創建的分區。
  Partitioner類用System,Collection.Concurrent名稱空間定義,並且有不同的變體。Create()方法接受實現了IList<T>類的數組或對象,以及Boolean類型的參數,返回一個不同的Partitioner類型。Create()方法有多個重載版本。
    var q2 = (from x in Partitioner.Create(data).AsParallel()
      where Math.Log(x) < 4
        select x).Average();

  也可以對AsParallel()方法接著調用WithExecutionMode()和WithDegreeOfParallelism()方法,來影響並行機制。WithExecutionMode()方法可以傳遞ParallelExecutionMode的一個Default值或者ForceParallelism值。預設情況下,並行LINQ避免使用系統開銷很高的並行機制。WithDegreeOfParallelism()方法,可以傳遞一個整數值,以指定應並行運行的最大任務數。如果查詢不應使用全部CPU,這個方法很有用。


  3.取消
  要取消長時間運行的查詢,可以給查詢添加WithCancellation()方法,並傳遞一個CancellationToken令牌作為參數。CancellationToken令牌從CancellationTokenSource類中創建。
  舉個例子,下麵的查詢在單獨的線程中運行,如果取消了查詢,在該線程中捕獲一個OperationCanceledException類型的異常。在主線程中,可以調用CancellationTokenSource類的Cancle()方法取消任務。

var data = SampleData();
          var watch = new Stopwatch();

          watch.Start();
          Console.WriteLine("filled array");
          var sum1 = (from x in data
                      where Math.Log(x) < 4
                      select x).Average();
          Console.WriteLine("sync result {0}", sum1);

          var cts = new CancellationTokenSource();
          
          Task.Factory.StartNew(() =>
            {
              try
              {
                var res = (from x in data.AsParallel().WithCancellation(cts.Token)
                           where Math.Log(x) < 4
                           select x).Average();
                Console.WriteLine("query finished, result: {0}", res);
              }
              catch (OperationCanceledException ex)
              {
                Console.WriteLine(ex.Message);
              }
            });
          watch.Stop();
          Console.WriteLine("async {0}, result: {1}", watch.ElapsedMilliseconds, "res");
          Console.WriteLine("query started");
          Console.Write("cancel? ");
          string input = Console.ReadLine();
          if (input.ToLower().Equals("y"))
          {
              cts.Cancel();
              Console.WriteLine("sent a cancel");
          }

          Console.WriteLine("press return to exit");
          Console.ReadLine();


  二.表達式樹
  在LINQ To Object 中,擴展方法需要將一個委托類型作為參數,這樣就可以將lambda表達式賦予參數。lambda表達式也可以賦予Expression<T>類型的參數,C#編譯器根據類型給lambda表達式定義不同的行為。如果類型是Expression<T>,編譯器就從lambda表達式中創建一個表達式樹,並存儲在程式集中。這樣就可以在運行期間分析表達式樹,併進行優化,以便查詢數據源。
  var racers = from r in Formula1.GetChampions()
    where r.Wins > 15 && (r.Country == "Brazil" || r.Country == "Austria")
      select r;

  這個查詢表達式使用了擴展方法Where(),Select()方法。Enumerable類定義了Where()方法,並將委托類型Func<T,bool>作為參數謂詞:
  public static IEnumerable<TSource> Where<TSource>(this IEnumerable<TSource> source,Func<TSource,bool> predicate);
  這樣,就可以把lambda表達式賦予委托predicate。

  除了使用委托之外,編譯器還會把表達式樹放在程式集中。表達式樹可以在運行期間讀取。表達式樹從派生自抽象基類Expression的類中構建。Expression和Expression<T>不同。繼承自Expression類的表達式類有BinaryExpression,ConstantExpression,InvocationExpression等。編譯器會從lambda表達式中創建表達式樹。
  例如,lambda表達式r.Country == "Brazil"使用了ParameterExpression,MemberExpression,ConstantExpression,MethodCallExpression,來創建一個表達式樹,並將該樹存儲在程式集中,之後在運行期間使用這個樹,創建一個用於底層數據源的優化查詢:

//DisplayTree方法在控制臺上圖形化的顯示表達式樹。其中傳遞一個Expression對象,並根據表達式的類型,把表達式的一些信息寫到控制臺上
                private static void DisplayTree(int indent, string message, Expression expression)
                {
                    string output = String.Format("{0} {1} ! NodeType: {2}; Expr: {3} ",
                          "".PadLeft(indent, '>'), message, expression.NodeType, expression);

                    indent++;
                    switch (expression.NodeType)
                    {
                        case ExpressionType.Lambda:
                            Console.WriteLine(output);
                            LambdaExpression lambdaExpr = (LambdaExpression)expression;
                            foreach (var parameter in lambdaExpr.Parameters)
                            {
                                DisplayTree(indent, "Parameter", parameter);
                            }
                            DisplayTree(indent, "Body", lambdaExpr.Body);
                            break;
                        case ExpressionType.Constant:
                            ConstantExpression constExpr = (ConstantExpression)expression;
                            Console.WriteLine("{0} Const Value: {1}", output, constExpr.Value);
                            break;
                        case ExpressionType.Parameter:
                            ParameterExpression paramExpr = (ParameterExpression)expression;
                            Console.WriteLine("{0} Param Type: {1}", output, paramExpr.Type.Name);
                            break;
                        case ExpressionType.Equal:
                        case ExpressionType.AndAlso:
                        case ExpressionType.GreaterThan:
                            BinaryExpression binExpr = (BinaryExpression)expression;
                            if (binExpr.Method != null)
                            {
                                Console.WriteLine("{0} Method: {1}", output, binExpr.Method.Name);
                            }
                            else
                            {
                                Console.WriteLine(output);
                            }
                            DisplayTree(indent, "Left", binExpr.Left);
                            DisplayTree(indent, "Right", binExpr.Right);
                            break;
                        case ExpressionType.MemberAccess:
                            MemberExpression memberExpr = (MemberExpression)expression;
                            Console.WriteLine("{0} Member Name: {1}, Type: {2}", output,
                               memberExpr.Member.Name, memberExpr.Type.Name);
                            DisplayTree(indent, "Member Expr", memberExpr.Expression);
                            break;
                        default:
                            Console.WriteLine();
                            Console.WriteLine("{0} {1}", expression.NodeType, expression.Type.Name);
                            break;
                    }
                }


                static void Main()
                {
                    Expression<Func<Racer, bool>> expression = r => r.Country == "Brazil" && r.Wins > 6;

                    DisplayTree(0, "Lambda", expression);

                }

  輸出:
  

  使用Expression<T>類型的一個例子是ADO.NET EF 和WCF數據服務的客戶端提供程式。這些技術用Expression<T>參數定義了擴展方法。這樣,訪問資料庫的LINQ提供程式就可以讀取表達式,創建一個運行期間優化的查詢,從資料庫中獲取數據。
  後面會單獨介紹表達式樹的使用。

  三.LINQ提供程式
  .NET包含幾個LINQ提供程式。LINQ提供程式為特定的數據源實現了標準的查詢操作符。LINQ提供程式也許會實現比LINQ定義的更多擴展方法,但至少要實現標準操作符。LINQ To XML實現了一些專門用於XML的方法,後面會詳細介紹。
  LINQ提供程式的實現方案是根據名稱空間和第一個參數的類型來選擇的。實現擴展方法的類的名稱空間必須是開放的,否則擴展方法就不在作用域內。在LINQ to Objects中定義的Where()方法的參數和LINQ To Entities中定義的Where()方法的參數不同:
    LINQ to Objects中定義的Where()方法:
      public static IEnumerable<TSource> Where<TSource>(this IEnumerable<TSource> source,Func<TSource,bool> predicate);
    LINQ To Entities中定義的Where()方法:
      public static IQueryable<TSource> Where<TSource>(this IQueryable<TSource> source,Expression<Func<TSource,bool>> predicate);
  這兩個類都在System.Linq的Syste,.Core程式集中實現。無論是用 Func<TSource,bool>傳遞參數,還是用Expression<Func<TSource,bool>>參數傳遞,lambda表達式都相同。只是編譯器的行為不同,它根據source參數來選擇。編譯器根據其參數選擇最匹配的方法。在ADO.NET EF中定義的ObjectContext類CreateQuery<T>()方法返回一個實現了IQueryable<TSource>介面的ObjectQuery<T>對象,因此EF使用Querable類的Where()方法。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 在WindowsServer2016上安裝ExchangeServer2016的體驗與以往版本的不同,不再是直接運行SETUP然後一路NEXT就可以順利完成的了。本人安裝過程中就遇到兩個意料之外的情況還好最後成功解決了,我把經驗分享出來給同樣被困住的人參考參考,希望有所幫助。 ...
  • WannaCry ransomware used in widespread attacks all over the world Customer Guidance for WannaCrypt attacks(Microsoft Security Response Center) How to ...
  • a.場景: 平時會使用百度網盤下載電影,但是用ios版的百度雲實在是有點慢,而平時不用windows,因此只能在linux上尋找解決之道. b.背景(我正在使用中): linux:ubuntu 14.04 64bit 瀏覽器:firefox41.0.2 c.安裝詳情: 1.安裝火狐插件flashgo ...
  • 摘 要:本文闡述了MySQL DDL 的問題現狀、pt-online-schema-change的工作原理,並實際利用pt-online-schema-change工具線上修改生產環境下1.6億級數據表結構。 在一個軟體生命周期中,我們都知道,前期的表結構設計是非常重要的,因為當表數據量一上來後再進 ...
  • 一.檢查和安裝與Perl相關的模塊 PT工具是使用Perl語言編寫和執行的,所以需要系統中有Perl環境。 依賴包檢查命令為: rpm -qa perl-DBI perl-DBD-MySQL perl-Time-HiRes perl-IO-Socket-SSL 如果有依賴包確實,可以使用下麵的命令安 ...
  • 要瞭解cgroup實現原理,必須先瞭解下vfs(虛擬文件系統).因為cgroup通過vfs向用戶層提供介面,用戶層通過掛載,創建目錄,讀寫文件的方式與cgroup交互. ...
  • 多個獨立的rtf文件合併時,如果文件紙張方向存在橫向 縱向不一致時,文件之間需要插入分節符 開始文件去掉最後一個"}",中間文件去掉第一個"{"及最後一個"}",最後一個去掉第一個"{";文件如存在紙張方向不一致則需要在兩個文件之間插入分節符。 參考:http://www.aiuxian.com/a ...
  • 系統有些埠是我們平時用不到但是又存在危險的埠,例如139、135、445等埠,windows預設是開著的,這時我們可以手動關閉這些埠!下邊為大家介紹一種通過windows組策略來一次性關閉所有想要關閉的危險埠 系統有些埠是我們平時用不到但是又存在危險的埠,例如139、135、445等端 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...