自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

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

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

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

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

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

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

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

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

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

PLINQ簡(jiǎn)介

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

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

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

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

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

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

處理PLINQ

調(diào)用PLINQ很簡(jiǎn)單,只需要在你的數(shù)據(jù)源中添加AsParallel擴(kuò)展,下面是一個(gè)從本地Northwind數(shù)據(jù)庫(kù)連接遠(yuǎn)程N(yùn)orthwind數(shù)據(jù)庫(kù),根據(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 

因?yàn)閮蓚€(gè)數(shù)據(jù)源都標(biāo)記了AsParallel(在連接時(shí),如果一個(gè)數(shù)據(jù)源使用了AsParallel,另一個(gè)也必須使用),因此將會(huì)使用PLINQ。

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

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

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

并行控制

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

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

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

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

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

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

  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ì)自動(dòng)退出循環(huán)。

如果在一個(gè)訂單(Order)上的處理過(guò)程不和另一個(gè)訂單上的處理過(guò)程共享狀態(tài),可以使用ForAll循環(huán)進(jìn)一步提高響應(yīng),F(xiàn)orAll可以用于支持Lambda表達(dá)式的PLINQ查詢結(jié)果集,它和For…Each循環(huán)不一樣,F(xiàn)or…Each只在程序的主線程中執(zhí)行的,而傳遞給ForAll方法的操作是在PLINQ查詢產(chǎn)生的獨(dú)立查詢線程上執(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é)果的順序依賴于各個(gè)線程不可預(yù)知的響應(yīng)時(shí)間,例如下面這個(gè)查詢是為了獲得將要先發(fā)貨的五個(gè)訂單。

  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)度

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

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

例如,如果我想將低于某一運(yùn)費(fèi)的所有訂單打包到一起處理,我可能會(huì)寫(xiě)下面這樣一個(gè)循環(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 

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

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

#p#

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

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

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

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

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

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

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

任何復(fù)雜的處理放在Lambda表達(dá)式中都會(huì)變得很難閱讀,因此你要經(jīng)常想到在你的Lambda表達(dá)式中調(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將集合中的成員分配給獨(dú)立的任務(wù),這些任務(wù)又被分配到所有處理核心上執(zhí)行,每個(gè)任務(wù)完成時(shí)釋放掉代碼,TPL調(diào)度器從執(zhí)行隊(duì)列中取出另一個(gè)任務(wù)開(kāi)始執(zhí)行,你也可以根據(jù)索引值使用For方法創(chuàng)建一個(gè)循環(huán)。

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

下面的示例為計(jì)算訂單總價(jià)的Order Detail對(duì)象創(chuàng)建并啟動(dòng)了一個(gè)Task,Task被添加到一個(gè)列表(List)中,后面的代碼循環(huán)檢索List中的結(jié)果,如果我需要一個(gè)未計(jì)算的結(jié)果,第二個(gè)循環(huán)將會(huì)暫停,直到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 

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

凡是遇到一個(gè)Task的輸出要依賴于另一個(gè)Task先完成的情況,你可以在Task之間創(chuàng)建依賴或?qū)ask分組,最簡(jiǎn)單的辦法是使用Wait方法,但它會(huì)導(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) 

一個(gè)更復(fù)雜的方法是使用Task對(duì)象的ContinueWith方法,當(dāng)其它Task完成時(shí),它觸發(fā)一個(gè)Task繼續(xù)運(yùn)行。下面的例子啟動(dòng)了多個(gè)線程,每個(gè)都計(jì)算訂單明細(xì)(Order Detail)的值,但都只有等到訂單明細(xì)上的其它操作完成后才能執(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 

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

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

#p#

出錯(cuò)時(shí)如何處理

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

  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 

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

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

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

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

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

其它調(diào)試功能

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

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

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

【編輯推薦】 

  1. Visual Studio自定義調(diào)整窗體的兩個(gè)小技巧
  2. Visual Studio 2010中關(guān)于C#的幾點(diǎn)改進(jìn)
  3. Visual Studio 2010及.Net 4新功能一覽
  4. 提高效率 用好Visual Studio 2010自定義代碼段
     
責(zé)任編輯:彭凡 來(lái)源: 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#語(yǔ)言

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-11-04 09:16:00

Visual Stud

2009-11-24 09:00:02

Visual Stud

2010-07-20 08:43:00

Visual Stud

2010-03-19 13:17:26

Parallel

2010-07-15 08:50:09

SharePointVisual Stud

2009-09-07 09:22:17

Visual Stud代碼片段

2009-11-10 13:43:37

Visual Stud

2010-01-06 09:41:55

Visual Stud

2009-03-17 08:56:57

Visual StudVS2010C++
點(diǎn)贊
收藏

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