偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

探秘.NET 4和Visual Studio 2010中的多核利用

原創(chuàng)
開發(fā) 后端
今天我們要介紹的是.NET 4和Visual Studio 2010中的多核利用,這也是未來性能提升的要點。

【51CTO經(jīng)典譯文】如果你想利用多核機器的強大計算能力,你需要使用PLINQ(并行LINQ),任務(wù)并行庫(Task Parallel Library,TPL)和Visual Studio2010中的新功能創(chuàng)建應(yīng)用程序。

51CTO向您推薦:《.NET 4多核并行編程指南

以前,如果你創(chuàng)建的多線程應(yīng)用程序有BUG,那要跟蹤起來是很麻煩的,但現(xiàn)在情況完全變了,感謝微軟為我們帶來了Microsoft Parallel Extensions for .NET(.NET并行擴展),它在.NET框架線程模型上提供了一個抽象層。

并行擴展遵循微軟在COM應(yīng)用程序中建立的事務(wù)管理和在數(shù)據(jù)訪問領(lǐng)域建立的實體框架和LINQ模型,它試圖通過給.NET框架中的復(fù)雜過程建立高級支持,以便將先進的技術(shù)帶給大眾,隨著多核處理器的普及,開發(fā)人員渴望他們的應(yīng)用程序可以利用所有處理器核心的計算能力。

你可以通過并行LINQ(PLINQ)和任務(wù)并行庫(Task Parallel Library,TPL)使用并行擴展的功能,它們都允許你為單核和多核計算機寫一套代碼,依靠.NET框架,***限度利用代碼執(zhí)行平臺的計算能力,并防止自行創(chuàng)建多線程應(yīng)用程序時常見的陷阱。

PLINQ擴展了LINQ查詢,它將單個查詢分解成多個并行運行的子查詢,TPL允許你創(chuàng)建并行運行的循環(huán),而不是一個接一個地運行,雖然PLINQ的聲明語法使創(chuàng)建并行進程更加簡單,但一般情況下,面向TPL的操作比PLINQ查詢更輕量級。

許多時候,選擇TPL還是PLINQ只是一種生活方式,如果喜歡并行循環(huán),而不是并行查詢,那么設(shè)計一個TPL解決方案比設(shè)計一個PLINQ解決方案更容易。

PLINQ簡介

對于商業(yè)應(yīng)用程序,只要LINQ查詢涉及到多個子查詢時,PLINQ就像金子一樣發(fā)光,如果你要連接本地數(shù)據(jù)庫某張表中的行和另一個遠程數(shù)據(jù)庫某張表中的行,PLINQ將非常有用,在這種情況下,LINQ必須在每個數(shù)據(jù)源上獨立運行子查詢,然后調(diào)和結(jié)果,PLINQ將會把這些子查詢分配給多個處理器核心,這些子查詢就可以同時執(zhí)行。實際上,你使用的處理器周期不是少了,而是更多了,當(dāng)然好處就是你可以更早得到結(jié)果,請閱讀“并行處理不會讓你的應(yīng)用程序變得更快”了解更多關(guān)于多線程應(yīng)用程序的行為。

并行處理不會讓你的應(yīng)用程序變得更快

關(guān)于多線程應(yīng)用程序最常見的一個誤解是,應(yīng)用程序線程越多,運行速度就越快,多啟動一個線程并不會導(dǎo)致Windows給你的應(yīng)用程序更多的處理周期,它只是把這些周期劃分給更多線程了,實際上,在單處理器計算機上,開啟多線程只會讓你的應(yīng)用程序變得更慢。

多線程只是讓你的應(yīng)用程序響應(yīng)更快,但它仍然要等待其它阻塞任務(wù)完成先,不過在等待期間,你可以利用多線程應(yīng)用程序的特點讓其它線程做一些別的事情。在單核機器上,如果線程未被阻塞,多個線程只能相互爭奪有限的處理周期。

多核處理器改變了這種狀況,在多核環(huán)境中,你可以讓W(xué)indows給你的應(yīng)用程序分配更多的處理周期,你不需要阻塞線程,所有線程都在它們自己的核心上執(zhí)行。并行擴展提供了編程結(jié)構(gòu),允許你告訴.NET框架應(yīng)用程序那些部分可以并行執(zhí)行。

即使在多核機器上,PLINQ也并不總是并行的查詢,有兩個原因,一是你的應(yīng)用程序并行運行不會總是更快,第二個原因是,即使你有一個抽象層管理你的線程,在并行處理時總會出現(xiàn)腳步不一致的情況,PLINQ會檢查一些不安全的條件,如果檢測到就不會進行并行查詢。我會指出PLINQ不會檢查的問題和條件,但使用PLINQ出了問題只有你自己負責(zé)處理。

處理PLINQ

調(diào)用PLINQ很簡單,只需要在你的數(shù)據(jù)源中添加AsParallel擴展,下面是一個從本地Northwind數(shù)據(jù)庫連接遠程Northwind數(shù)據(jù)庫,根據(jù)客戶(customer)信息查詢訂單(Orders)的示例:

  1. Dim ords As System.Linq.ParallelQuery(Of ParallelExtensions.Order)  
  2. ords = From c In le.Customers.AsParallel Join o In re.Orders.AsParallel  
  3.            On c.CustomerID Equals o.CustomerID  
  4.        Where c.CustomerID = "ALFKI" 
  5.        Select o 

因為兩個數(shù)據(jù)源都標(biāo)記了AsParallel(在連接時,如果一個數(shù)據(jù)源使用了AsParallel,另一個也必須使用),因此將會使用PLINQ。

和普通的LINQ查詢一樣,PLINQ查詢使用延遲處理,即等到你要真正使用數(shù)據(jù)時,它才會開始檢索,這意味著即使LINQ查詢聲明了是并行的,在你要處理結(jié)果前不會發(fā)生并行處理,除非使用下面這樣的代碼塊:

  1. For Each ord As Order In ords  
  2.   ord.RequiredDate.Value.AddDays(2)  
  3. Next 

在后臺,PLINQ將使用一個線程執(zhí)行For …Each循環(huán)中的代碼,而其它線程可能被用來執(zhí)行子查詢,***可以使用64個線程,請閱讀“并行控制”材料了解這種行為的更多信息。

并行控制

本文認為并行LINQ(PLINQ)總是好的,例如,首先選擇是否要并行運行,然后決定如何將多個子查詢分配給多個線程,你可以使用With*擴展控制PLINQ的行為。

在使用調(diào)試工具的時候,你會發(fā)現(xiàn)PLINQ不是并行執(zhí)行查詢的,你可以傳遞ParallelExecutionMode .ForceParallelism值給WithExecutionMode方法讓其強制并行執(zhí)行查詢。

  1. ords = From o In le.Orders.AsParallel.  
  2.           WithExecutionMode(ParallelExecutionMode.ForceParallelism) 

如果你想指定線程的數(shù)量(例如,你想讓一或多個處理核心閑置),你可以使用WithDegreeOfParallelism方法,下面的代碼示例將線程數(shù)限制為3。

  1. ords = From o In le.Orders.AsParallel.  
  2.            WithDegreeOfParallelism(3) 

你也可以使用cancellation結(jié)束處理過程,首先創(chuàng)建一個CancellationTokenSource對象,然后將其傳遞給WithCancellation擴展。

  1. Dim ctx As New System.Threading.CancellationTokenSource  
  2. ords = From o In le.Orders.AsParallel.  
  3.             WithCancellation(ctx.Token)  
  4.     Where o.RequiredDate > Now  
  5.     Select o  
  6.       
  7. For Each ord As Order In ords  
  8.   totFreight += ord.Freight  
  9.   If totFreight > FreightChargeLimit Then  
  10.         ctx.Cancel()  
  11.   End If  
  12. Next 

如果你正在處理For…Each循環(huán)中的PLINQ查詢結(jié)果,調(diào)用cancellation會自動退出循環(huán)。

如果在一個訂單(Order)上的處理過程不和另一個訂單上的處理過程共享狀態(tài),可以使用ForAll循環(huán)進一步提高響應(yīng),F(xiàn)orAll可以用于支持Lambda表達式的PLINQ查詢結(jié)果集,它和For…Each循環(huán)不一樣,F(xiàn)or…Each只在程序的主線程中執(zhí)行的,而傳遞給ForAll方法的操作是在PLINQ查詢產(chǎn)生的獨立查詢線程上執(zhí)行的。

  1. ords.ForAll(Sub(ord)  
  2.                  ord.RequiredDate.Value.AddDays(2)  
  3.               End Sub) 

此外,F(xiàn)or…Each循環(huán)是在它自己的線程中串行執(zhí)行的,而ForAll中的代碼是在檢索訂單的線程上并行執(zhí)行的。

管理順序

雖然和SQL類似,但PLINQ不保證順序,PLINQ子查詢返回結(jié)果的順序依賴于各個線程不可預(yù)知的響應(yīng)時間,例如下面這個查詢是為了獲得將要先發(fā)貨的五個訂單。

  1. ords = From o In re.Orders.AsParallel  
  2.         Where o.RequiredDate > Now  
  3.         Select o  
  4.         Take (5) 

PLINQ給TPL中的功能添加查詢分析和標(biāo)準(zhǔn)查詢操作,TPL提供管理操作系統(tǒng)底層線程需要的基本的結(jié)構(gòu)和調(diào)度

圖 1 PLINQ給TPL中的功能添加查詢分析和標(biāo)準(zhǔn)查詢操作,TPL提供管理操作系統(tǒng)底層線程需要的基本的結(jié)構(gòu)和調(diào)度

如果不保證順序,我將獲得一個隨機的訂單(Orders)數(shù)據(jù)集,它們可能是(也可能不是)應(yīng)該先發(fā)貨的五個訂單,為了確保得到前五個訂單,我需要在查詢中增加一個Order By子句,按照日期對查詢結(jié)果進行排序,當(dāng)然這樣就會丟掉PLINQ的一些好處。

因為結(jié)果來自多個線程,難免不會出現(xiàn)異常,PLINQ不能明白“上一條”和“下一條”的概念,如果在你的循環(huán)中剛好要用到下一條項目的值時,完全有可能會遭遇錯誤的處理,為了讓訂單中的項目按照原始數(shù)據(jù)源中的順序處理,你需要在查詢中增加AsOrdered擴展。

例如,如果我想將低于某一運費的所有訂單打包到一起處理,我可能會寫下面這樣一個循環(huán):

  1. For Each ord As Order In ords  
  2.    totFreight += ord.Freight  
  3.    If totFreight > FreightChargeLimit Then  
  4.      Exit For  
  5.    End If  
  6.    shipOrders.Add(ord)  
  7. Next 

由于并行處理返回的項目順序不可預(yù)知,因此進入批處理的訂單可能是隨機的,為了保證按照原始數(shù)據(jù)源中的順序處理返回的結(jié)果,我必須給數(shù)據(jù)源加上AsOrdered擴展。

  1. ords = From o In re.Orders.AsParallel.AsOrdered  
  2.         Where o.RequiredDate > Now  
  3.         Select o 

#p#

TPL(任務(wù)并行庫)介紹

如果你的處理不是由LINQ查詢驅(qū)動的,你可以使用借鑒了PLINQ的TPL技術(shù),從根本上看,TPL讓你創(chuàng)建可并行執(zhí)行的循環(huán),如果你的計算機是四核的,一個循環(huán)可能用1/3的時間就完成了。

如果不使用TPL,你可能會像下面這樣處理Orders集合中的所有元素:

  1. For Each o As Order In le.Orders  
  2.   o.RequiredDate.Value.AddDays(2)  
  3. Next 

如果使用TPL,你調(diào)用Parallel類的ForEach方法,通過Lambda表達式來處理集合中的項目:

  1. System.Threading.Tasks.Parallel.ForEach(  
  2.    le.Orders, Sub(o)  
  3.                   o.RequiredDate.Value.AddDays(2)  
  4.                 End Sub) 

通過使用Parallel ForEach,每個方法的實例可以在獨立的處理器上同時處理,如果每個操作需要1毫秒,并且有足夠的處理器存在,所有的訂單就可以在1毫秒內(nèi)處理,而不是1毫秒乘以訂單數(shù)量的時間。

任何復(fù)雜的處理放在Lambda表達式中都會變得很難閱讀,因此你要經(jīng)常想到在你的Lambda表達式中調(diào)用下面這樣一些方法:

  1. System.Threading.Tasks.Parallel.ForEach(  
  2.    le.Orders, Sub(o)  
  3.                   ExtendOrders(o)  
  4.                 End Sub)  
  5. ...  
  6. Sub ExtendOrders(ByVal o As Order)  
  7.         o.RequiredDate.Value.AddDays(2)  
  8. End Sub 

從本質(zhì)上講,TPL將集合中的成員分配給獨立的任務(wù),這些任務(wù)又被分配到所有處理核心上執(zhí)行,每個任務(wù)完成時釋放掉代碼,TPL調(diào)度器從執(zhí)行隊列中取出另一個任務(wù)開始執(zhí)行,你也可以根據(jù)索引值使用For方法創(chuàng)建一個循環(huán)。

當(dāng)你創(chuàng)建自定義任務(wù)時你才會感覺到TPL的強大之處,任務(wù)創(chuàng)建好后使用它的Start方法啟動,但它更容易使用Task類的靜態(tài)工廠對象(Factory),它的StartNew方法可以創(chuàng)建并啟動任務(wù)(Task),你只需要通過一個Lambda表達式就可以使用StartNew方法,如果你的函數(shù)要返回一個值,你可以使用Task對象的Generic版本指定返回的類型。

下面的示例為計算訂單總價的Order Detail對象創(chuàng)建并啟動了一個Task,Task被添加到一個列表(List)中,后面的代碼循環(huán)檢索List中的結(jié)果,如果我需要一個未計算的結(jié)果,第二個循環(huán)將會暫停,直到Task完成。

  1. Dim CalcTask As System.Threading.   
  2.  Tasks.Task(Of Decimal)  
  3. Dim CalcTasks As New List(Of System.  
  4.  Threading.Tasks.Task(Of Decimal))  
  5. For Each ord As Order_Detail In   
  6.  le.Order_Details  
  7.  Dim od As Order_Detail = ord 
  8.  CalcTask = System.Threading.  
  9.   Tasks.Task(Of Decimal).  
  10.                   Factory.StartNew(Function() CalcValue(od))  
  11.    CalcTasks.Add(CalcTask)  
  12. Next  
  13.  
  14. Dim totResult As Decimal  
  15. For Each ct As System.Threading.Tasks.Task(Of Decimal) In CalcTasks  
  16.     totResult += ct.Result  
  17. Next 

如果我足夠幸運,在我需要結(jié)果前,Task總是先完成,即使不走運,也要比按順序運行每個Task更早得到結(jié)果。

凡是遇到一個Task的輸出要依賴于另一個Task先完成的情況,你可以在Task之間創(chuàng)建依賴或?qū)ask分組,最簡單的辦法是使用Wait方法,但它會導(dǎo)致你的應(yīng)用程序停止執(zhí)行,直到所有Task全部完成。

  1. Dim tsks() As System.Threading.Tasks.Task = {  
  2.   Task(Of Decimal).Factory.StartNew(Function() CalcValue(le.Order_Details(0))),  
  3.   Task(Of Decimal).Factory.StartNew(Function() CalcValue(le.Order_Details(1)))  
  4.                                             }  
  5. System.Threading.Tasks.Task.WaitAll(tsks) 

一個更復(fù)雜的方法是使用Task對象的ContinueWith方法,當(dāng)其它Task完成時,它觸發(fā)一個Task繼續(xù)運行。下面的例子啟動了多個線程,每個都計算訂單明細(Order Detail)的值,但都只有等到訂單明細上的其它操作完成后才能執(zhí)行。

  1. For Each ordd As Order_Detail In le.Order_Details  
  2.   Dim od As Order_Detail = ordd 
  3.   Dim adjustedDiscount As New Task(Sub() AdjustDiscount(od))  
  4.   Dim calcedValue As Task(Of Long) =   
  5.      adjustedDiscount.ContinueWith(Of Long)(Function() CalcValue(od))  
  6.   adjustedDiscount.Start  
  7. Next 

并行堆棧窗口提供了一個可視化視圖,顯示了當(dāng)前執(zhí)行的線程的附加信息

圖 2 并行堆棧窗口提供了一個可視化視圖,顯示了當(dāng)前執(zhí)行的線程的附加信息

#p#

出錯時如何處理

在多個處理器上同時執(zhí)行多個線程也會造成異常出現(xiàn)得更頻繁,任何線程上一旦發(fā)生異常,整個應(yīng)用程序都將掛起,給AggregateException對象添加的錯誤處理也會增加,通過這個對象的InnerExceptions屬性允許你查看每個線程的異常。

  1. Dim Messages As New System.Text.StringBuilder  
  2. Try  
  3.       'PLINQ or TPL processing  
  4. Catch aex As AggregateException  
  5.   For Each ex As Exception In aex.InnerExceptions  
  6.     Messages.Append(ex.Message & "; ")  
  7.    Next  
  8. End Try 

注意這里沒有使用Catch語句,你需要檢查InnerExceptions的類型,確定每個線程究竟拋出的是什么異常。

調(diào)試并發(fā)線程變得更加有趣,因為異??赡茈S一個PLINQ查詢中的循環(huán)出現(xiàn),解決這個問題可能需要重構(gòu)PLINQ查詢,幸運的是,Visual Studio 2010包括了額外的工具調(diào)式并行錯誤。

并行堆棧窗口(Parallel Stacks)超越了舊的線程窗口,線程窗口只能提供一個視圖,而并行堆棧窗口可以顯示所有正在執(zhí)行的線程,例如,它默認允許你同時查看多個線程的調(diào)用堆棧,你可以放大顯示內(nèi)容,也可以過濾只顯示指定的線程,更重要的是,如果你使用TPL,你可以切換到基于任務(wù)的視圖(對應(yīng)于你代碼中的Task對象),或方法視圖(顯示調(diào)用方法的任務(wù)),但使用并行任務(wù)窗口(Parallel Tasks)可能更有用,因為它圍繞Task組織任務(wù),這個窗口不僅顯示當(dāng)前運行的任務(wù),已調(diào)度和等待運行的任務(wù)也會顯示(顯示在狀態(tài)[Status]列),你可以通過檢查當(dāng)前運行的Task是否在等待其它任務(wù),從而確定Task之間的依賴關(guān)系。

在早期的Visual Studio版本中,要一步一步調(diào)式多線程程序是一場噩夢,因為調(diào)試器要從一個線程中的當(dāng)前語句跳轉(zhuǎn)到另一個線程的當(dāng)前語句,并行任務(wù)(Parallel Task)允許你凍結(jié)或解凍與Task相關(guān)的線程,在調(diào)試時控制哪一個線程先運行。

一起使用這兩個窗口可以簡化并行處理問題的診斷,例如,Visual Studio現(xiàn)在檢測到一個死鎖時,它會自動打破死鎖,當(dāng)調(diào)式器檢測到兩個或多個Task不能處理時(因為相互都在等待對方釋放鎖定的對象),Visual Studio將實施凍結(jié)處理,就好像你遇到一個斷點似的,并行任務(wù)窗口將顯示每個Task在等待的對象,以及它占有的線程,并行堆棧窗口的方法視圖可視化顯示了發(fā)生死鎖時哪個Task調(diào)用了哪個方法。

其它調(diào)試功能

除了這些工具外,Visual Studio還包含了其它幾個用于調(diào)式并行處理的功能,在遍歷你的代碼時,當(dāng)你的鼠標(biāo)移到一個Task對象上時,彈出一個提示窗口,顯示該任務(wù)的Id,關(guān)聯(lián)的方法和它當(dāng)前的狀態(tài)(如,等待執(zhí)行)等詳細信息,進一步展開該提示,可以看到該Task的屬性值,包括它的結(jié)果。在觀察窗口(Watch)中檢查Task的InternalCurrent屬性,可以得到當(dāng)前正在執(zhí)行的Task的信息,任務(wù)調(diào)度器(TaskScheduler)的提示展開后可以看到它管理的所有Task。

合理使用PLINQ,TPL和Visual Studio提供的功能,無論你的應(yīng)用程序運行在什么計算機上,你都可以利用所有處理器的計算能力。

原文標(biāo)題:Exploit Multi-Core Processors with .NET 4 and Visual Studio 2010

【編輯推薦】 

  1. Visual Studio自定義調(diào)整窗體的兩個小技巧
  2. Visual Studio 2010中關(guān)于C#的幾點改進
  3. Visual Studio 2010及.Net 4新功能一覽
  4. 提高效率 用好Visual Studio 2010自定義代碼段
     
責(zé)任編輯:彭凡 來源: 51CTO
相關(guān)推薦

2010-03-31 14:13:23

Visual Stud.Net Framew

2009-03-10 10:21:05

災(zāi)難恢復(fù)Restart Manvs

2009-11-10 09:13:47

Visual Stud

2009-11-19 09:59:47

Visual Stud

2009-09-02 16:21:17

Visual BasiC#語言

2010-04-08 15:14:59

Visual StudASP.NET 4.

2009-12-02 09:43:38

Visual Stud

2010-03-02 09:10:41

Visual Stud

2009-09-03 09:10:24

Visual Stud

2010-04-01 15:10:06

Visual Stud

2010-04-01 14:51:52

Visual Stud

2009-09-07 09:22:17

Visual Stud代碼片段

2009-11-10 13:43:37

Visual Stud

2009-11-04 09:16:00

Visual Stud

2009-11-24 09:00:02

Visual Stud

2010-03-19 13:17:26

Parallel

2010-07-15 08:50:09

SharePointVisual Stud

2010-07-20 08:43:00

Visual Stud

2010-01-06 09:41:55

Visual Stud

2010-04-15 08:40:00

UML建模Visual Stud
點贊
收藏

51CTO技術(shù)棧公眾號