在Parallel中使用DbSet.Add()發(fā)現(xiàn)的一系列多線程問(wèn)題和解決思路詳解
發(fā)現(xiàn)問(wèn)題
需求很簡(jiǎn)單,大致就是要批量往數(shù)據(jù)庫(kù)寫(xiě)數(shù)據(jù),于是打算用Parallel并行的方式寫(xiě)入,希望能利用計(jì)算機(jī)多核特性加快程序執(zhí)行速度。想的很美好,于是快速擼了類(lèi)似下面的一串代碼:
using (var db = new SmsEntities()) { Parallel.For(0, 1000, (i) => { db.MemberCard.Add(new MemberCard() { CardNo = "NO_" + i.ToString(), Banlance = 0, CreateTime = DateTime.Now, Name = "Test_" + i.ToString(), Status = 1 }); }); db.SaveChanges(); }
可意外的是竟然無(wú)情的報(bào)錯(cuò)了:
奇葩的是當(dāng)我再次刷新的時(shí)候異常又不一樣了,于是連著刷新好多次,總結(jié)出現(xiàn)過(guò)的異常有下面這些:
1、 未將對(duì)象引用設(shè)置到對(duì)象的實(shí)例。
2、 已添加了具有相同鍵的項(xiàng)。
3、 集合已修改;可能無(wú)法執(zhí)行枚舉操作。
4、 一個(gè) EdmType 不能多次映射到 CLR 類(lèi)。EdmType“SmsModel.MemberCard”映射了一次以上。
其中1和2是出現(xiàn)最多的,而且所有異常都是出現(xiàn)在Add的時(shí)候,各種吃瓜表情~沒(méi)辦法,接著一一斷點(diǎn)調(diào)試,還是沒(méi)找出原因,出于進(jìn)度考慮,換成了另一種方案,也就是用DbSet的AddRange方法。先在Parallel中累加出一個(gè)實(shí)體List,然后一次性添加到DbSet中,代碼演變?yōu)?
List<MemberCard> list = new List<MemberCard>(); using (var db = new SmsEntities()) { var result = Parallel.For(0, 1000, (i) => { list.Add(new MemberCard() { CardNo = "NO_" + i.ToString(), Banlance = 0, CreateTime = DateTime.Now, Name = "Test_" + i.ToString(), Status = 1 }); }); if (result.IsCompleted) { db.MemberCard.AddRange(list); db.SaveChanges(); } }
然后編譯、測(cè)試,沒(méi)問(wèn)題,就先放著了。
分析問(wèn)題
第二天到公司心里還在糾結(jié)這個(gè)問(wèn)題,于是打開(kāi)頁(yè)面輸入生成的數(shù)據(jù)量1000(真實(shí)項(xiàng)目中的循環(huán)次數(shù)是手動(dòng)輸入的),點(diǎn)按鈕提交,嗯,又吃瓜般的異常了…:
心想昨天測(cè)試都好好的?。ㄆ鋵?shí)昨天輸入的是10,心虛臉...),沒(méi)辦法,上斷點(diǎn)吧,一看嚇一跳:
明明循環(huán)1000次,結(jié)果只有971條數(shù)據(jù),而且里面還有為null的,經(jīng)過(guò)多次調(diào)試發(fā)現(xiàn)這是一個(gè)隨機(jī)現(xiàn)象,Count是隨機(jī)的null也是隨機(jī)的,有時(shí)出現(xiàn)有時(shí)沒(méi)有,初步判斷這是一個(gè)在多線程情況下引發(fā)的一個(gè)資源調(diào)配異常。So,上MSDN看了一下List的介紹,最后面“線程安全”寫(xiě)著:
一切貌似都清楚了,于是打算驗(yàn)證一下結(jié)果,加上了鎖,測(cè)試結(jié)果為:
list里面也沒(méi)有再出現(xiàn)null了,確認(rèn)是因?yàn)槎嗑€程安全引起的異常。于是想起昨天那個(gè)問(wèn)題是否也是同樣的問(wèn)題,再上MSDN搜了一下DbContext類(lèi)和DbSet類(lèi),都是這樣說(shuō)的:
接著就給dbcontext上了鎖,測(cè)試,這次總算如我所料,完美運(yùn)行。但是不解的是最初那幾個(gè)異常是如何產(chǎn)生的,List中雖然數(shù)量不夠也存在為null的對(duì)象,但是并沒(méi)有直接爆出異常?,F(xiàn)在只知道是線程問(wèn)題,再詳細(xì)的也搞不清楚,有知道的大神還麻煩指點(diǎn)一下。
尋找解決方案并驗(yàn)證結(jié)論
也想過(guò)用Partitioner分區(qū)來(lái)做,但是仔細(xì)一想,雖然分區(qū)內(nèi)部是單線程,但是區(qū)與區(qū)之間還是多線程的,如果分的太細(xì)也就失去了Parallel的意義,只得另尋出路。還好Framework為我們也提供了一些線程安全的泛型集合(比如ConcurrentBag、ConcurrentQueue等),不過(guò)其本質(zhì)還是用了鎖,于是就綜合做了一下單線程list、多線程list加鎖、多線程ConcurrentBag、多線程ConcurrentQueue的性能對(duì)比,結(jié)果如下:
循環(huán)1000次時(shí):
循環(huán)10000次時(shí):
循環(huán)100000次時(shí):
得出結(jié)論就是,在執(zhí)行次數(shù)超大時(shí)用線程安全類(lèi)型會(huì)更慢,在執(zhí)行次數(shù)較少時(shí)線程安全類(lèi)型也沒(méi)什么優(yōu)勢(shì)。
解決問(wèn)題
最后在經(jīng)過(guò)仔細(xì)測(cè)試驗(yàn)證和考慮項(xiàng)目實(shí)際需求(幾乎不可能一次10000)后,去繁從簡(jiǎn),回歸原始,用最簡(jiǎn)單直白的寫(xiě)法單線程循環(huán)來(lái)完成。雖然一番折騰下來(lái)還是回到最初,但是這過(guò)程中讓我發(fā)現(xiàn)了意料之外問(wèn)題,然后找到了原因,然后測(cè)試驗(yàn)證,最終得到了最優(yōu)解決方案。還是那句話,填完坑,你就比之前更強(qiáng)大了!
上一篇:C#的Process類(lèi)調(diào)用第三方插件實(shí)現(xiàn)PDF文件轉(zhuǎn)SWF文件
欄 目:C#教程
本文標(biāo)題:在Parallel中使用DbSet.Add()發(fā)現(xiàn)的一系列多線程問(wèn)題和解決思路詳解
本文地址:http://mengdiqiu.com.cn/a1/C_jiaocheng/6177.html
您可能感興趣的文章
- 01-10C#通過(guò)反射獲取當(dāng)前工程中所有窗體并打開(kāi)的方法
- 01-10C#實(shí)現(xiàn)Winform中打開(kāi)網(wǎng)頁(yè)頁(yè)面的方法
- 01-10C#實(shí)現(xiàn)由四周向中心縮小的窗體退出特效
- 01-10Extjs4如何處理后臺(tái)json數(shù)據(jù)中日期和時(shí)間
- 01-10C#實(shí)現(xiàn)將窗體固定在顯示器的左上角且不能移動(dòng)的方法
- 01-10C#中DataGridView常用操作實(shí)例小結(jié)
- 01-10C#編程獲取資源文件中圖片的方法
- 01-10asp.net中XML如何做增刪改查操作
- 01-10C#實(shí)現(xiàn)在Form里面內(nèi)嵌dos窗體的方法
- 01-10C#利用反射技術(shù)實(shí)現(xiàn)去掉按鈕選中時(shí)的邊框效果


閱讀排行
- 1C語(yǔ)言 while語(yǔ)句的用法詳解
- 2java 實(shí)現(xiàn)簡(jiǎn)單圣誕樹(shù)的示例代碼(圣誕
- 3利用C語(yǔ)言實(shí)現(xiàn)“百馬百擔(dān)”問(wèn)題方法
- 4C語(yǔ)言中計(jì)算正弦的相關(guān)函數(shù)總結(jié)
- 5c語(yǔ)言計(jì)算三角形面積代碼
- 6什么是 WSH(腳本宿主)的詳細(xì)解釋
- 7C++ 中隨機(jī)函數(shù)random函數(shù)的使用方法
- 8正則表達(dá)式匹配各種特殊字符
- 9C語(yǔ)言十進(jìn)制轉(zhuǎn)二進(jìn)制代碼實(shí)例
- 10C語(yǔ)言查找數(shù)組里數(shù)字重復(fù)次數(shù)的方法
本欄相關(guān)
- 01-10C#通過(guò)反射獲取當(dāng)前工程中所有窗體并
- 01-10關(guān)于ASP網(wǎng)頁(yè)無(wú)法打開(kāi)的解決方案
- 01-10WinForm限制窗體不能移到屏幕外的方法
- 01-10WinForm繪制圓角的方法
- 01-10C#實(shí)現(xiàn)txt定位指定行完整實(shí)例
- 01-10WinForm實(shí)現(xiàn)仿視頻播放器左下角滾動(dòng)新
- 01-10C#停止線程的方法
- 01-10C#實(shí)現(xiàn)清空回收站的方法
- 01-10C#通過(guò)重寫(xiě)Panel改變邊框顏色與寬度的
- 01-10C#實(shí)現(xiàn)讀取注冊(cè)表監(jiān)控當(dāng)前操作系統(tǒng)已
隨機(jī)閱讀
- 08-05dedecms(織夢(mèng))副欄目數(shù)量限制代碼修改
- 01-10delphi制作wav文件的方法
- 08-05織夢(mèng)dedecms什么時(shí)候用欄目交叉功能?
- 04-02jquery與jsp,用jquery
- 01-10使用C語(yǔ)言求解撲克牌的順子及n個(gè)骰子
- 01-11ajax實(shí)現(xiàn)頁(yè)面的局部加載
- 01-10SublimeText編譯C開(kāi)發(fā)環(huán)境設(shè)置
- 01-11Mac OSX 打開(kāi)原生自帶讀寫(xiě)NTFS功能(圖文
- 01-10C#中split用法實(shí)例總結(jié)
- 08-05DEDE織夢(mèng)data目錄下的sessions文件夾有什