<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
System.Linq名稱空間中包含的類ParallelEnumerable可以分解查詢的工作,使其分佈在多個執行緒上。
儘管Enumerable類給IEnumerable<T>介面定義了擴充套件方法,但ParallelEnumerable類的大多數擴充套件方法是ParallerQuery<TSource>類的擴充套件。例如,AsParallel()方法,它擴充套件了IEnumerable<T>介面,返回ParallelQuery<T>類,所以正常的集合類可以以平行方式查詢。
下面演示並行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基礎(二)(https://www.jb51.net/article/244215.htm)中的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類,查詢是分割區的,以便多個執行緒可以同時處理該查詢。集合可以分為多個部分,其中每個部分由不同的執行緒處理。完成分割區的工作後,就需要合併,獲得所有部分的總和。
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,這個方法很有用。
要取消長時間執行的查詢,可以給查詢新增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提供程式就可以讀取表示式,建立一個執行期間優化的查詢,從資料庫中獲取資料。
後面會單獨介紹表示式樹的使用。
.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()方法。
到此這篇關於c#中LINQ的基本用法的文章就介紹到這了。希望對大家的學習有所幫助,也希望大家多多支援it145.com。
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45