A. 何時使用 Parallel.ForEach,何時使用 PLINQ
當需要為多核機器進行優化的時候,最好先檢查下你的程序是否有處理能夠分割開來進行並行處理。(例如,有一個巨大的數據集合,其中的元素需要一個一個進行彼此獨立的耗時計算)。.netframework4中提供了Parallel.ForEach和PLINQ來幫助我們進行並行處理,本文探討這兩者的差別及適用的場景。Parallel.ForEachParallel.ForEach是foreach的多線程實現,他們都能對IEnumerable類型對象進行遍歷,Parallel.ForEach的特殊之處在於它使用多線程來執行循環體內的代碼段。Parallel.ForEach最常用的形式如下:(IEnumerablesource,Actionbody)PLINQPLINQ也是一種對數據進行並行處理的編程模型,它通過LINQ的語法來實現類似Parallel.ForEach的多線程並行棚宴處理。場景一:簡單數據之獨立操作的並行處理(使用Parallel.ForEach)示例代碼:(IEnumerablesource,Actionaction){Parallel.ForEach(source,element=>action(element));}理由:1.雖然PLINQ也提供了一個類似的ForAll介面,但它對於簡單的獨立操作太重量化了。2.使用Parallel.ForEach你還能夠設定ParallelOptions.MaxDegreeOfParalelism參數(指定最多需要多少個線程),這樣當ThreadPool資源匱乏(甚至當可用線程數Movie){varProcessedMovie=Movie.AsParallel().AsOrdered().Select(frame=>ConvertToGrayscale(frame));foreach(){//}}理由:1.Parallel.ForEach實現起來需鏈友銀要繞一些彎路,首先你需要使用以下的重載在方法:(IEnumerablesource,Actionbody)這個重載的Action多包含了index參數,這樣你在輸出的時候就能利用這個值來維持原先的序列順序。請看下面的例子:告薯publicstaticdouble[]PairwiseMultiply(double[]v1,double[]v2){varlength=Math.Min(v1.Length,v2.Lenth);double[]result=newdouble[length];Parallel.ForEach(v1,(element,loopstate,elementIndex)=>result[elementIndex]=element*v2[elementIndex]);returnresult;}你可能已經意識到這里有個明顯的問題:我們使用了固定長度的數組。如果傳入的是IEnumerable那麼你有4個解決方案:(1)調用IEnumerable.Count()來獲取數據長度,然後用這個值實例化一個固定長度的數組,然後使用上例的代碼。(2);,.(沒看懂貼原文)(3)第三種方式是採用返回一個哈希集合的方式,這種方式下通常需要至少2倍於傳入數據的內存,所以處理大數據時請慎用。(4)自己實現排序演算法(保證傳入數據與傳出數據經過排序後次序一致)2.相比之下PLINQ的AsOrdered方法如此簡單,而且該方法能處理流式的數據,從而允許傳入數據是延遲實現的(lazymaterialized)場景三:流數據之並行處理(使用PLINQ)PLINQ能輸出流數據,這個特性在一下場合非常有用:1.結果集不需要是一個完整的處理完畢的數組,即:任何時間點下內存中僅保持數組中的部分信息2.你能夠在一個單線程上遍歷輸出結果(就好像他們已經存在/處理完了)示例:publicstaticvoidAnalyzeStocks(IEnumerableStocks){varStockRiskPortfolio=Stocks.AsParallel().AsOrdered().Select(stock=>new{Stock=stock,Risk=ComputeRisk(stock)}).Where(stockRisk=>ExpensiveRiskAnalysis(stockRisk.Risk));foreach(){SomeStockComputation(stockRisk.Risk);//}}這里使用一個單線程的foreach來對PLINQ的輸出進行後續處理,通常情況下foreach不需要等待PLINQ處理完所有數據就能開始運作。PLINQ也允許指定輸出緩存的方式,具體可參照PLINQ的WithMergeOptions方法,及ParallelMergeOptions枚舉場景四:處理兩個集合(使用PLINQ)PLINQ的Zip方法提供了同時遍歷兩個集合並進行結合元算的方法,並且它可以與其他查詢處理操作結合,實現非常復雜的機能。示例:(IEnumerablea,IEnumerableb){returna.AsParallel().AsOrdered().Select(element=>ExpensiveComputation(element)).Zip(b.AsParallel().AsOrdered().Select(element=>DifferentExpensiveComputation(element)),(a_element,b_element)=>Combine(a_element,b_element));}示例中的兩個數據源能夠並行處理,當雙方都有一個可用元素時提供給Zip進行後續處理(Combine)。Parallel.ForEach也能實現類似的Zip處理:(IEnumerablea,IEnumerableb){varnumElements=Math.Min(a.Count(),b.Count());varresult=newT[numElements];Parallel.ForEach(a,(element,loopstate,index)=>{vara_element=ExpensiveComputation(element);varb_element=DifferentExpensiveComputation(b.ElementAt(index));result[index]=Combine(a_element,b_element);});returnresult;}當然使用Parallel.ForEach後你就得自己確認是否要維持原始序列,並且要注意數組越界訪問的問題。場景五:線程局部變數Parallel.ForEach提供了一個線程局部變數的重載,定義如下:(IEnumerablesource,FunclocalInit,Funcbody,ActionlocalFinally)使用的示例:publicstaticListFiltering(IEnumerablesource){varresults=newList();using(SemaphoreSlimsem=newSemaphoreSlim(1)){Parallel.ForEach(source,()=>newList(),(element,loopstate,localStorage)=>{boolfilter=filterFunction(element);if(filter)localStorage.Add(element);returnlocalStorage;},(finalStorage)=>{lock(myLock){results.AddRange(finalStorage)};});}returnresults;}線程局部變數有什麼優勢呢?請看下面的例子(一個網頁抓取程序):(){WebClientwebclient=newWebClient();Parallel.ForEach(urls,(url,loopstate,index)=>{webclient.DownloadFile(url,filenames[index]+".dat");Console.WriteLine("{0}:{1}",Thread.CurrentThread.ManagedThreadId,url);});}通常第一版代碼是這么寫的,但是運行時會報錯「System.NotSupportedException->/Ooperations.」。這是因為多個線程無法同時訪問同一個WebClient對象。所以我們會把WebClient對象定義到線程中來:publicstaticvoidBAD_DownloadUrls(){Parallel.ForEach(urls,(url,loopstate,index)=>{WebClientwebclient=newWebClient();webclient.DownloadFile(url,filenames[index]+".dat");Console.WriteLine("{0}:{1}",Thread.CurrentThread.ManagedThreadId,url);});}修改之後依然有問題,因為你的機器不是伺服器,大量實例化的WebClient迅速達到你機器允許的虛擬連接上限數。線程局部變數可以解決這個問題:(){Parallel.ForEach(urls,()=>newWebClient(),(url,loopstate,index,webclient)=>{webclient.DownloadFile(url,filenames[index]+".dat");Console.WriteLine("{0}:{1}",Thread.CurrentThread.ManagedThreadId,url);returnwebclient;},(webclient)=>{});}這樣的寫法保證了我們能獲得足夠的WebClient實例,同時這些WebClient實例彼此隔離僅僅屬於各自關聯的線程。雖然PLINQ提供了ThreadLocal對象來實現類似的功能:publicstaticvoiddownloadUrl(){varwebclient=newThreadLocal(()=>newWebClient());varres=urls.AsParallel().ForAll(url=>{webclient.Value.DownloadFile(url,host[url]+".dat"));Console.WriteLine("{0}:{1}",Thread.CurrentThread.ManagedThreadId,url);});}但是請注意:ThreadLocal相對而言開銷更大!場景五:退出操作(使用Parallel.ForEach)Parallel.ForEach有個重載聲明如下,其中包含一個ParallelLoopState對象:(IEnumerablesource,Actionbody)ParallelLoopState.Stop()提供了退出循環的方法,這種方式要比其他兩種方法更快。這個方法通知循環不要再啟動執行新的迭代,並盡可能快的推出循環。ParallelLoopState.IsStopped屬性可用來判定其他迭代是否調用了Stop方法。示例:publicstaticbooleanFindAny(IEnumerableTSpace,Tmatch)whereT:IEqualityComparer{varmatchFound=false;Parallel.ForEach(TSpace,(curValue,loopstate)=>{if(curValue.Equals(match)){matchFound=true;loopstate.Stop();}});returnmatchFound;}ParallelLoopState.Break()通知循環繼續執行本元素前的迭代,但不執行本元素之後的迭代。最前調用Break的起作用,並被記錄到ParallelLoopState.LowestBreakIteration屬性中。這種處理方式通常被應用在一個有序的查找處理中,比如你有一個排序過的數組,你想在其中查找匹配元素的最小index,那麼可以使用以下的代碼:(IEnumerableTSpace,Tmatch)whereT:IEqualityComparer{varloopResult=Parallel.ForEach(source,(curValue,loopState,curIndex)=>{if(curValue.Equals(match)){loopState.Break();}});varmatchedIndex=loopResult.LowestBreakIteration;returnmatchedIndex.HasValue?matchedIndex:-1;}
B. 區塊鏈技術有哪些應用
《關於深化公共資源交易平台整合共享的指導意見》(國辦函〔2019〕41號)文件指出需優化見證、場所、信息、檔案、專家抽取等服務。但目前公共資源交易過程見證以人工現場見證為主,見證力度有限,對人力資源佔用高,見證效果有限。傳統的數字化見證系統因其中心化特點事後數據容易被篡改,且數據在存儲、遷移過程容易損壞或丟失,從安全性可用性上都存在一定缺陷。
利用區塊鏈分布式、難篡改、可追溯的特點對每個交易環節產生的數據進行固化存證,通過時間戳技術、摘要演算法、電子簽名技術准確記錄數據產生的時間、內容、數據來源。根據區塊鏈的技術特性對於簡單的結構化數據可直接將數據保存在區塊鏈上,對於非結構化的版式文件、視頻、音頻的等大文件通過區塊鏈保存其摘要信息,原文件通過分布式文件存儲服務進行保存。當交易存在糾紛或者問題的時候,區塊鏈可提供一套可信的交易過程數據,釐清交易主體各方的責任。實現全環節風險防控、全過程可溯可查、全方位服務提升的目標。
促進電子保函費率合理化
促進投標企業金融服務和企業融資
促進電子保函費率合理化
目前電子投標保證金擔保保函已在招投標領域有一定的應用,為投標企業解決了投標保證金方面的資金佔用問題。但因目前各家金融機構沒有可靠的投標人歷史投標行為數據,無法對不同投標人的違約風險進行判別,導致對投標人收取的擔保服務都採用固定費率,使少部分違約風險高的投標人擔保成本侍輪輪被分攤到大部分違約風險低的投標人身上,在一定程度上提高了大部分投標人保函費率。
目前是否使用電子保函由投標人自主選擇,而費率又是投標人的主要選擇依據,若通過區塊鏈匯聚共享投標人履約記錄,分析不同投標人履約風險,為不同投標人提供不同擔保費率,既降低金融機構風險,又可降低大部分投標人的使用成本促進投標保函的使用,在一定程度上也可促進投標人重約定守信用,維護招投標市場秩序。
促進投標企業金融服務
投標人的投標行為分散在各個交易中心,單純地將數據匯聚至一個中心化的信息系統又存在數據被篡改風險(不可信),老信有價值的投標人交易行為數據無法安全可靠地匯聚、共享。通過區塊鏈技術匯聚多個交易中心投標人,歷史投標、中標、違約、違規等行為記錄為金融機構對投標人的在招投標細分行業的信用評估提供數據支撐。
解決中標企業融資問題
傳統的企業貸款主要通過評估企業償債能力:抵押物、審計過的報表、持續性盈利等有要求,但是大多桐拿數中小企業根本拿不出這些「證明」,融資難、融資貴成為招投標活動中許多中小企業面臨的問題。使用過去的方法已經走不通了,要破解中小企業融資難問題,唯有依靠新技術和新工具。藉助區塊鏈不可篡改的特點,匯聚多個交易中心一手業務數據,結合大數據分析技術構建可信投標人畫像。一方面提金融機構高風控水平,挖掘優質投標企業,另一方面為投標企業降低貸款門檻,優化服務體驗。
借鑒供應鏈金融模式,招標人是政府部門、國家企事業單位具有很好信用的核心企業,中標人作為供應商獲得的中標合同被金融機構認為是一種優質的資產向金融機構申請貸款。傳統紙質模式下存在訂單合同造假風險且流程煩瑣,中心化系信息系統又需要運營方有極強的權威性。區塊鏈的分布式賬本及難篡改特點將有助於上述問題的解決,將招標人與投標人的合同簽署及後續金融服務環節都在區塊鏈上實現,既解決數據可信問題又降低了整個系統對中心化權威機構的依賴。
C. 數字貨幣平台開發,什麼是數字貨幣
概念:數字貨幣是指對貨幣進行數字化
數字貨幣/虛擬貨幣既然困孫叫"貨幣",必定要有貨幣的功能才對,貨幣最大的功能是支付和匯率,現行的數字貨幣有沒有這方面的功能呢,或者說計劃呢?是有的,有些幣種,比方NPC、Zrcoin在創世的時分是以什物財物來做背書的,這么的幣具有和什物價值相關的特點,是價值的最真實體現,也是最有也許向支付運用改變的幣種,幣的壽數和將來的遠景也是會不錯的.
比特幣之類的貨幣像萊特幣、維卡幣,元寶幣,天元仙寶(天元幣)等,是一種依靠密碼技術和校驗技術來創建,分發和維持的數字貨幣。密碼貨幣的特點在其運用了點對點技術且每個人都有發行它。
說到數字貨幣就不得不提比特幣的發展歷史了,畢竟比特幣類似於數字貨幣也把各種山寨幣的都帶動了起來!
2008年11月1日中本聰在metzdowd的密碼學郵件組列表中發表了一篇論文,論文描述了比特幣的電子現金系統。
2009年,不受中央和任何金融機構控制的比特幣誕生。比特幣用揭露散布總賬擺脫了第三方機構的制約,中本聰稱之為「區域鏈」其貨幣特徵包括:去中心化、全世界流通、專屬所有權、低交易費用、無隱藏成本、跨平台挖掘等。
2009年1月3日,中本聰製作了比特幣世界的第一個區塊「創世區塊」。比特幣的創始人中本聰在創世區塊里留下一句永不可修改的話:「The Times 03/Jan/2009 Chancellor on brink of second lout for
banks(2009年1月3日,財政大臣正處於實施第二輪銀行緊急援助的邊緣)當時正是英國的財政大臣達林被迫考慮第二次出手紓解銀行危機的時刻,這句話是泰晤士報當天的頭版文章標題。
2010年5月21日,佛羅里達程序員用1萬比特幣購買了價值25美元的披薩優惠券,隨著這筆交易誕生了比特幣第一個公允匯率。
2010年7月,第一個比特幣平台成立,新用戶暴增,價格暴漲。
2011年2月,比特幣價源尺早格首次達到1美元,此後與英鎊、巴西雷亞爾、波蘭茲羅提匯兌交易平台開張。
2012年,瑞波發布,其作為數字貨幣,利用區塊鏈轉移各國外匯。
2013年,比特幣暴漲。美國財政部發布了虛擬貨幣個人管理條例,首次闡明虛擬貨幣釋義。
2014年,以中國為代表的礦機產業鏈日益成熟,同年,美國IT界認識到區塊鏈對於數字領域的跨時代創新意義。
2014年,央行成立專門研究團隊,並於2015年初進一步充實力量,對數字貨幣發行和業務運行框架、數字貨幣的關鍵技術等進行了深入研究,已取得階段性成果。
2014年3月9日上午,谷歌創意主管傑雷德·科恩(Jared Cohen)本周在參加SXSW大會時表示,與比特幣類似的數字加密貨幣將長期存在。科恩和谷歌董事長埃里克·施密特(Eric Schmidt)在此次大會上推廣了合著的《新數字時代》一書。
2014年7月,中國國務院頒布了《關於積極推進「互聯網+」行動的指導意見》把「互聯網+」列入「十三五」雹雀規劃產業的發展主線,提出拓展網路經濟空間,發展分享經濟,促進互聯網和經濟社會融合發展。實施國家大數據戰略,推進數據資源開放共享。
2015年英國首先提出將「沙盒」這一理念應用到金融監管領域,為創新企業提供了縮小版的真實環境。建立沙盒機制能讓政府在可控的范圍內進行多種創新,給區塊鏈行業帶來更多機會。
2015年11月,納斯達克和Chain合作的區塊鏈技術新項目Linq以利於基於區塊鏈的發行交易平台完成了第一筆私募股權交易。
2015年12月,美國納斯達克首次在個股交易商使用區塊鏈技術。
2015年年末,以R3CEV為代表的區塊鏈聯盟成立,短時間內吸引了超過40家國際銀行業巨頭加盟。
2016年是「區塊鏈元年」。各路風投投入了上十億美金資金投入到區塊鏈的創業公司和項目,這一年區塊鏈價值被全世界認可,區塊鏈概念被不斷驗證。
2016年1月中國人民銀行數字貨幣研討會在北京召開,進一步明確央行發行數字貨幣的戰略目標,做好關鍵技術攻關,研究數字貨幣的多場景應用,爭取早日推出央行發行的數字貨幣。未來的數字貨幣可能在區塊鏈上建立賬本,不會被人篡改,而電子支付只是單向記賬。越來越多的金融機構開始關注數字貨幣背後的創新技術——「區塊鏈(blockchain)」。
2016年10月,工信部發布《中國區塊鏈技術和應用發展白皮書》,這是首個落地的區塊鏈官方指導文件。年末,國務院印發《「十三五」國家信息化規劃》,區塊鏈與大數據、人工智慧、機器深度學習等新技術,成為國家布局重點。區塊鏈技術可能重塑政府運行方式,並使其變得更高效。在政府信任度降低的時代,信息必須以更加透明和負責的方式進行存儲。區塊鏈為政府在治理、安全和法律領域提供了多種可能性。
2016年12月, 浙商銀行也正式上線基於區塊鏈技術移動數字匯票產品,搭建了基於區塊鏈技術移動數字匯票平台。
2016年12月15日,央行數字票據基於區塊鏈的全生命周期的登記流轉和基於數字貨幣的票款對付(DVP)結算功能已經全部實現,顯示數字貨幣在數字票據場景的應用驗證落地。
2017年,區塊鏈技術將突破實驗室並進入真實的市場環境。
2017年7月1日,上海第一家無人超市落地,24小時營業,沒有一個員工
2017年1月10日,中國郵政儲蓄銀行和IBM聯合宣布推出基於區塊鏈的資產託管系統,該系統於2016年10月上線,已在真實業務環境中順利執行了上百筆交易。這是中國銀行業將區塊鏈技術應用於銀行核心業務系統的首次成功實踐。
2017年1月25日,中國央行推動的基於區塊鏈的數字票據交易平台已測試成功。
(銀行相爭湧入區塊鏈領域的原因很簡單,銀行通過使用自己許可式區塊鏈來記錄所有客戶交易,提高交易效率,不用再把交易數據記錄到各種不同類型並且很快過時的軟體中。)
2017年7月2日,深圳實現自動收銀,全程再無收銀員
2017年7月7日到8日,於漢堡舉行的二十國集團首腦峰會上普金錶示,數字技術的發展將為全球經濟向新的工業秩序轉型提供支撐。
這不得不提到區塊鏈技術了!比特幣這幾年火了,估計中本聰也沒想到比特幣竟然這么貴,09年出來的比特幣,現在17年的比特幣接近2萬元/1個,發展之快和價值之高,簡直難以想像。這讓我想起了當年用10000個比特幣購買20多美元的披薩,真不愧是世界上最貴的交易,也許在當時不值錢,如果用現在的計算方式你會震驚!一萬個比特幣*現在價格兩萬/一個,猜猜多少?……
算出來了嗎?當時也把我嚇住了,沒錯,接近2個億,的確是最貴的披薩,這一般人享受不起啊,/汗!顏/。比特幣之類的幣種發展速度之快,真的超乎想像!(如果需要數字貨幣開發區塊鏈交易所系統可以找我)
那我為什麼說不得不提到區塊鏈技術呢?區塊鏈作為比特幣的底層核心技術,它的應用價值比比特幣更高,相當於在比特幣中,金礦-礦工-礦車,它相當於礦車,說的簡單點,金礦好比資產的,對於礦工來說就是從一個地方運到另一個地方,礦工要維持金礦的運轉肯定會挖礦,但是礦車呢?礦車除了運金礦,還有很多功能,他可以做其他很多領域的事情,這些都可以用區塊鏈技術。區塊鏈就像一個資料庫賬本,記載所有的交易記錄。
把區塊鏈想像成一個比特幣的公共賬本,這個賬本:
1.存放在互聯網的各個比特幣節點上,每個節點都有一份完整的備份
2.裡面記錄著自比特幣誕生以來的所有比特幣轉賬交易
3.賬本是分區塊存儲的,每一塊包含一部分交易記錄。每一個區塊都會記錄著前一區塊的id,形成一個鏈狀結構,因而稱為區塊鏈
4.當你要發起一筆比特幣交易的時候只需把交易信息廣播到p2p網路中,礦工把你的交易信息記錄成一個新的區塊連到區塊鏈上,交易就完成了。
由中本聰提出的數字貨幣和區塊鏈到現在為止已經進入了爆發式的增長,那麼區塊鏈技術能應用到哪些領域呢?
涉及資產領域,無論是房產、汽車等實物資產,還是健康、名譽等無形資產,都能利用該技術完成登記、交易、追蹤。可以這樣說,任何缺乏信任的生產生活領域,區塊鏈交易所技術都將有用武之地。
隨著區塊鏈技術在中國的發展,在商業機構、政府和用戶的共同推動下,已逐漸形成一個交易、監管不斷優化的區塊鏈環境。致力於區塊鏈技術研究和應用的企業和個人將會有更多的財富機會去創造更大的商業價值,實現區塊鏈技術理念的美好前景。
關於數字貨幣的將來,推動數字貨幣開展,數字貨幣創業者,出資者和極客供給最具有價值工作前沿資訊,在這里將造就不計其數的百萬、千萬、億萬富翁,當金融遇上互聯網,再加上復利倍增方式,財富將超越你的夢想。(對數字貨幣比特幣交易所感興趣的可以加我微QKL17999/q+2729656186/電+13823153926)
D. c# 大數據量問題
3000條數據,不脊斗態是3000萬條,不用考銷宏慮得這么精細。
更何況,就算是3000萬條,也不過是幾十秒的時間,一般從資料庫讀取到展示,如果是3000萬條的話,也要這么多時間的,如櫻源果電腦配置更加低的話,更久。
所以foreach+foreach也沒什麼,3000條,一秒內就完了。
E. linq to sql 和entity framework 相比,哪個性能更好
單從實現方式上來講應該是entity framework效率更高,雖然我從沒用過,也很少使用linq,entity framework是一套orm框架,類似的還有很慧信多,而linq to sql 是在orm的基礎上再去使用linq特橡睜性實現某個功能,相對基礎語法,linq的使用只是減少了代碼,卻影響了性能。所以梁碧歲我認為linq to sql性能會差一些。但是,這也取決於兩套東西內部具體的實現和應用,尤其應用在大數據量的場景中才能比較出差異。各有優劣,這樣看來這個提問有點過於籠統了哦。 所以具體的應用還要看具體的需求,單獨講性能的話還是基礎語法來的最快。
F. C#:在類的靜態方法代碼中能否使用this對象引用,為什麼
this指的是類實碰老例化的當前對象,靜態方法是通過類調用的,不需要實例化;既然不需要實例化,就沒有當前對象;罩衫既然沒有當前對象,所以不能使用this 關鍵字。
C#四種用法:
用法一 this代表當前類的實例對象
用法二 用this串聯構造函數
用法三 為原始類型物吵腔擴展方法
用法四 索引器(基於索引器封裝EPList,用於優化大數據下頻發的Linq查詢引發的程序性能問題,通過索引從list集合中查詢數據)
G. asp.net mvc linq 查詢 出現類型不能強制轉換,怎麼解決
如果數據列中沒有圖片流等大數據,你就老老實棗埋實用m就行,如果你想只選擇出必要的列,可以在御虛select的時候new Movie出來並只填充要選擇的列凳拆螞,或者 自定義一個ViewModel 。
H. 何時使用 Parallel.ForEach,何時使用 PLINQ
當需要為多核機器肆磨進行優化的時候,最好先檢查下你的程序是否有處理能夠分割開來進行並行處理。(物彎例如,有一個巨大的數據集合,其中的元素需要一個一個進行彼此獨立的耗時計算)。
.net framework 4 中提供了 Parallel.ForEach 和 PLINQ 來幫助我們進行並行處理,本文探討這兩者的差別及適用的場景。
Parallel.ForEach
Parallel.ForEach 是 foreach 的多線程實現,他們都能對 IEnumerable<T> 類型對象進行遍歷,Parallel.ForEach 的特殊之處在於它使用多線程來執行循環體內的代碼段。
Parallel.ForEach 最常用的形式如下:
public static ParallelLoopResult ForEach<TSource>(
IEnumerable<TSource> source,
Action<TSource> body)
PLINQ
PLINQ 也是一種對數據進行並行處理的編程模型,它通過 LINQ 的語法來實現類似 Parallel.ForEach 的多線程並行處理。
場景一:簡單數據 之 獨立操作的並行處理(使用 Parallel.ForEach)
示例代碼:
public static void IndependentAction(IEnumerable<T> source, Action<T> action)
{
Parallel.ForEach(source, element => action(element));
}
理由:
1. 雖然 PLINQ 也提供了一個類似的 ForAll 介面,但它對於簡單的獨立操作太重量化了。
2. 使用 Parallel.ForEach 你還能夠設定
ParallelOptions.MaxDegreeOfParalelism 參數(指定最多需要多少個線程),這樣當 ThreadPool
資源匱乏(甚至當可用線程數<MaxDegreeOfParalelism)的時候, Parallel.ForEach
依然能夠順利運行,並且當後續有更多可用線程出現時,Parallel.ForEach 也能及時地利用這些線程。PLINQ
只能通過WithDegreeOfParallelism 方法來要求固定的線程數,即:要求了幾個就是幾個,不會多也不會少。
場景二:順序數據 之 並行處理(使用 PLINQ 來維持數據順序)
當輸出的數據序列需要保裂螞斗持原始的順序時採用 PLINQ 的 AsOrdered 方法非常簡單高效。
示例代碼:
public static void GrayscaleTransformation(IEnumerable<Frame> Movie)
{
var ProcessedMovie =
Movie
.AsParallel()
.AsOrdered()
.Select(frame => ConvertToGrayscale(frame));
foreach (var grayscaleFrame in ProcessedMovie)
{
// Movie frames will be evaluated lazily
}
}
理由:
1. Parallel.ForEach 實現起來需要繞一些彎路,首先你需要使用以下的重載在方法:
public static ParallelLoopResult ForEach<TSource >(
IEnumerable<TSource> source,
Action<TSource, ParallelLoopState, Int64> body)
這個重載的 Action 多包含了 index 參數,這樣你在輸出的時候就能利用這個值來維持原先的序列順序。請看下面的例子: public static double [] PairwiseMultiply(double[] v1, double[] v2)
{
var length = Math.Min(v1.Length, v2.Lenth);
double[] result = new double[length];
Parallel.ForEach(v1, (element, loopstate, elementIndex) =>
result[elementIndex] = element * v2[elementIndex]);
return result;
}
你可能已經意識到這里有個明顯的問題:我們使用了固定長度的數組。如果傳入的是 IEnumerable 那麼你有4個解決方案:
(1) 調用 IEnumerable.Count() 來獲取數據長度,然後用這個值實例化一個固定長度的數組,然後使用上例的代碼。
(2) The second option would be to materialize the original
collection before using it; in the event that your input data set is
prohibitively large, neither of the first two options will be
feasible.(沒看懂貼原文)
(3) 第三種方式是採用返回一個哈希集合的方式,這種方式下通常需要至少2倍於傳入數據的內存,所以處理大數據時請慎用。
(4) 自己實現排序演算法(保證傳入數據與傳出數據經過排序後次序一致)
2. 相比之下 PLINQ 的 AsOrdered 方法如此簡單,而且該方法能處理流式的數據,從而允許傳入數據是延遲實現的(lazy materialized)
場景三:流數據 之 並行處理(使用 PLINQ)
PLINQ 能輸出流數據,這個特性在一下場合非常有用:
1. 結果集不需要是一個完整的處理完畢的數組,即:任何時間點下內存中僅保持數組中的部分信息
2. 你能夠在一個單線程上遍歷輸出結果(就好像他們已經存在/處理完了)
示例:
public static void AnalyzeStocks(IEnumerable<Stock> Stocks)
{
var StockRiskPortfolio =
Stocks
.AsParallel()
.AsOrdered()
.Select(stock => new { Stock = stock, Risk = ComputeRisk(stock)})
.Where(stockRisk => ExpensiveRiskAnalysis(stockRisk.Risk));
foreach (var stockRisk in StockRiskPortfolio)
{
SomeStockComputation(stockRisk.Risk);
// StockRiskPortfolio will be a stream of results
}
}
這里使用一個單線程的 foreach 來對 PLINQ 的輸出進行後續處理,通常情況下 foreach 不需要等待 PLINQ 處理完所有數據就能開始運作。
PLINQ 也允許指定輸出緩存的方式,具體可參照 PLINQ 的 WithMergeOptions 方法,及 ParallelMergeOptions 枚舉
場景四:處理兩個集合(使用 PLINQ)
PLINQ 的 Zip 方法提供了同時遍歷兩個集合並進行結合元算的方法,並且它可以與其他查詢處理操作結合,實現非常復雜的機能。
示例:
public static IEnumerable<T> Zipping<T>(IEnumerable<T> a, IEnumerable<T> b)
{
return
a
.AsParallel()
.AsOrdered()
.Select(element => ExpensiveComputation(element))
.Zip(
b
.AsParallel()
.AsOrdered()
.Select(element => DifferentExpensiveComputation(element)),
(a_element, b_element) => Combine(a_element,b_element));
}
示例中的兩個數據源能夠並行處理,當雙方都有一個可用元素時提供給 Zip 進行後續處理(Combine)。
Parallel.ForEach 也能實現類似的 Zip 處理:
public static IEnumerable<T> Zipping<T>(IEnumerable<T> a, IEnumerable<T> b)
{
var numElements = Math.Min(a.Count(), b.Count());
var result = new T[numElements];
Parallel.ForEach(a,
(element, loopstate, index) =>
{
var a_element = ExpensiveComputation(element);
var b_element = DifferentExpensiveComputation(b.ElementAt(index));
result[index] = Combine(a_element, b_element);
});
return result;
}
當然使用 Parallel.ForEach 後你就得自己確認是否要維持原始序列,並且要注意數組越界訪問的問題。
場景五:線程局部變數
Parallel.ForEach 提供了一個線程局部變數的重載,定義如下:
public static ParallelLoopResult ForEach<TSource, TLocal>(
IEnumerable<TSource> source,
Func<TLocal> localInit,
Func<TSource, ParallelLoopState, TLocal,TLocal> body,
Action<TLocal> localFinally)
使用的示例: public static List<R> Filtering<T,R>(IEnumerable<T> source)
{
var results = new List<R>();
using (SemaphoreSlim sem = new SemaphoreSlim(1))
{
Parallel.ForEach(source,
() => new List<R>(),
(element, loopstate, localStorage) =>
{
bool filter = filterFunction(element);
if (filter)
localStorage.Add(element);
return localStorage;
},
(finalStorage) =>
{
lock(myLock)
{
results.AddRange(finalStorage)
};
});
}
return results;
}
線程局部變數有什麼優勢呢?請看下面的例子(一個網頁抓取程序): public static void UnsafeDownloadUrls ()
{
WebClient webclient = new WebClient();
Parallel.ForEach(urls,
(url,loopstate,index) =>
{
webclient.DownloadFile(url, filenames[index] + ".dat");
Console.WriteLine("{0}:{1}", Thread.CurrentThread.ManagedThreadId, url);
});
}
通常第一版代碼是這么寫的,但是運行時會報錯「System.NotSupportedException -> WebClient
does not support concurrent I/O operations.」。這是因為多個線程無法同時訪問同一個 WebClient
對象。所以我們會把 WebClient 對象定義到線程中來: public static void BAD_DownloadUrls ()
{
Parallel.ForEach(urls,
(url,loopstate,index) =>
{
WebClient webclient = new WebClient();
webclient.DownloadFile(url, filenames[index] + ".dat");
Console.WriteLine("{0}:{1}", Thread.CurrentThread.ManagedThreadId, url);
});
}
修改之後依然有問題,因為你的機器不是伺服器,大量實例化的 WebClient 迅速達到你機器允許的虛擬連接上限數。線程局部變數可以解決這個問題: public static void downloadUrlsSafe()
{
Parallel.ForEach(urls,
() => new WebClient(),
(url, loopstate, index, webclient) =>
{
webclient.DownloadFile(url, filenames[index]+".dat");
Console.WriteLine("{0}:{1}", Thread.CurrentThread.ManagedThreadId, url);
return webclient;
},
(webclient) => { });
}
這樣的寫法保證了我們能獲得足夠的 WebClient 實例,同時這些 WebClient 實例彼此隔離僅僅屬於各自關聯的線程。
雖然 PLINQ 提供了 ThreadLocal<T> 對象來實現類似的功能:
public static void downloadUrl()
{
var webclient = new ThreadLocal<WebClient>(()=> new WebClient ());
var res =
urls
.AsParallel()
.ForAll(
url =>
{
webclient.Value.DownloadFile(url, host[url] +".dat"));
Console.WriteLine("{0}:{1}", Thread.CurrentThread.ManagedThreadId, url);
});
}
但是請注意:ThreadLocal<T> 相對而言開銷更大!
場景五:退出操作 (使用 Parallel.ForEach)
Parallel.ForEach 有個重載聲明如下,其中包含一個 ParallelLoopState 對象:
public static ParallelLoopResult ForEach<TSource >(
IEnumerable<TSource> source,
Action<TSource, ParallelLoopState> body)
ParallelLoopState.Stop() 提供了退出循環的方法,這種方式要比其他兩種方法更快。這個方法通知循環不要再啟動執行新的迭代,並盡可能快的推出循環。
ParallelLoopState.IsStopped 屬性可用來判定其他迭代是否調用了 Stop 方法。
示例:
public static boolean FindAny<T,T>(IEnumerable<T> TSpace, T match) where T: IEqualityComparer<T>
{
var matchFound = false;
Parallel.ForEach(TSpace,
(curValue, loopstate) =>
{
if (curValue.Equals(match) )
{
matchFound = true;
loopstate.Stop();
}
});
return matchFound;
}
ParallelLoopState.Break() 通知循環繼續執行本元素前的迭代,但不執行本元素之後的迭代。最前調用 Break
的起作用,並被記錄到 ParallelLoopState.LowestBreakIteration
屬性中。這種處理方式通常被應用在一個有序的查找處理中,比如你有一個排序過的數組,你想在其中查找匹配元素的最小
index,那麼可以使用以下的代碼:
public static int FindLowestIndex<T,T>(IEnumerable<T> TSpace, T match) where T: IEqualityComparer<T>
{
var loopResult = Parallel.ForEach(source,
(curValue, loopState, curIndex) =>
{
if (curValue.Equals(match))
{
loopState.Break();
}
});
var matchedIndex = loopResult.LowestBreakIteration;
return matchedIndex.HasValue ? matchedIndex : -1;
}
I. 如何把linq查詢結果賦值給hashset c
類似的問題被提了很多遍了,你可以從根子上這樣看:
HashSet<T>的構造方法之一是HashSet<T>(IEnumerable<T> collection),這說明哈希集可以在構造時通過傳入一個有著*元素類型為T的可枚舉的集合*完成初始化。
如1樓所示,List<string> _dd其實本身就繼承有IEnumerable<string>介面,所以不要使顫滲用Linq的cast系列方法,你只需要一行:
1
HashSet<string> _ss=new HashSet<string>(_dd);
大多數的集合類型都可以這樣通過構造來初枯洞銷始化。
由於這種轉換是結構上的轉換而非值的轉換,所以絕大多數情況下都沒游需要new一下目標對象,指望List<T>自己提供轉換結構的方法是不靠譜的。
此外要說一句,你說的「地球人都知道」沒有錯,但是它還真的不慢喲。要知道,即使是使用上述構造方法,本質上來說CLR也是通過一個一個枚舉_dd的元素並添加到_ss去的,兩種方法在代碼量上有差距,但是執行效率上幾乎相等的。(因為編譯時代碼會自動優化的)
真的要使用大數據(如含有10萬string的List<string>),請勿使用上述兩種方法(因為都慢),你需要學習Parallel系列的並行處理,可以理解為一種能夠利用到所有CPU核心的多線程處理。