程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> 在Parallel中運用DbSet.Add()發現的一系列多線程問題和處理思緒詳解

在Parallel中運用DbSet.Add()發現的一系列多線程問題和處理思緒詳解

編輯:C#入門知識

在Parallel中運用DbSet.Add()發現的一系列多線程問題和處理思緒詳解。本站提示廣大學習愛好者:(在Parallel中運用DbSet.Add()發現的一系列多線程問題和處理思緒詳解)文章只能為提供參考,不一定能成為您想要的結果。以下是在Parallel中運用DbSet.Add()發現的一系列多線程問題和處理思緒詳解正文


發現問題

需求很復雜,大致就是要批量往數據庫寫數據,於是計劃用Parallel並行的方式寫入,希望能應用計算機多核特性放慢順序執行速度。想的很美妙,於是疾速撸了相似上面的一串代碼:

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();
}

可不測的是居然無情的報錯了:

奇葩的是當我再次刷新的時分異常又不一樣了,於是連著刷新好屢次,總結呈現過的異常有上面這些:

1、 未將對象援用設置到對象的實例。

2、 已添加了具有相反鍵的項。

3、 集合已修正;能夠無法執行枚舉操作。

4、 一個 EdmType 不能屢次映射到 CLR 類。EdmType“SmsModel.MemberCard”映射了一次以上。

其中1和2是呈現最多的,而且一切異常都是呈現在Add的時分,各種吃瓜表情~沒方法,接著逐個斷點調試,還是沒找出緣由,出於進度思索,換成了另一種方案,也就是用DbSet的AddRange辦法。先在Parallel中累加出一個實體List,然後一次性添加到DbSet中,代碼演化為:

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();
}
}

然後編譯、測試,沒問題,就先放著了。

剖析問題

第二天到公司心裡還在糾結這個問題,於是翻開頁面輸出生成的數據量1000(真實項目中的循環次數是手動輸出的),點按鈕提交,嗯,又吃瓜般的異常了…:

心想昨天測試都好好的啊(其實昨天輸出的是10,心虛臉...),沒方法,上斷點吧,一看嚇一跳:

明明循環1000次,後果只要971條數據,而且外面還無為null的,經過屢次調試發現這是一個隨機景象,Count是隨機的null也是隨機的,有時呈現有時沒有,初步判別這是一個在多線程狀況下引發的一個資源分配異常。So,上MSDN看了一下List的引見,最前面“線程平安”寫著:

一切貌似都清楚了,於是計劃驗證一下後果,加上了鎖,測試後果為:

list外面也沒有再呈現null了,確認是由於多線程平安惹起的異常。於是想起昨天那個問題能否也是異樣的問題,再上MSDN搜了一下DbContext類和DbSet類,都是這樣說的:

接著就給dbcontext上了鎖,測試,這次總算如我所料,完滿運轉。但是不解的是最初那幾個異常是如何發生的,List中雖然數量不夠也存在為null的對象,但是並沒有直接爆出異常。如今只知道是線程問題,再詳細的也搞不清楚,有知道的大神還費事指點一下。

尋覓處理方案並驗證結論

也想過用Partitioner分區來做,但是細心一想,雖然分區外部是單線程,但是區與區之間還是多線程的,假如分的太細也就得到了Parallel的意義,只得另尋出路。還好Framework為我們也提供了一些線程平安的泛型集合(比方ConcurrentBag、ConcurrentQueue等),不過其實質還是用了鎖,於是就綜合做了一下單線程list、多線程list加鎖、多線程ConcurrentBag、多線程ConcurrentQueue的功能比照,後果如下:

循環1000次時:

循環10000次時:

循環100000次時:

得出結論就是,在執行次數超大時用線程平安類型會更慢,在執行次數較少時線程平安類型也沒什麼優勢。

處理問題

最後在經過細心測實驗證和思索項目實踐需求(簡直不能夠一次10000)後,去繁從簡,回歸原始,用最復雜直白的寫法單線程循環來完成。雖然一番折騰上去還是回到最初,但是這進程中讓我發現了預料之外問題,然後找到了緣由,然後測實驗證,最終失掉了最優處理方案。還是那句話,填完坑,你就比之前更弱小了!

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved