看板 C_Sharp 關於我們 聯絡資訊
===前情提要=== 目前在重整資料庫的資料(約2,800萬筆),所以必須一筆一筆爬 資料爬出來後會做兩種處理,再建立新的資料庫 資料庫使用mongodb,先把整個collection做findall,再丟入foreach的迴圈去跑 用了兩個foreach,為省略版面,以下code只寫一個foreach作為範本 ===方案A,單執行緒=== var result = coll.FindAll(); foreach(var doc in result) { 工作!(); } 結果: 工作A處理效能:20筆/秒 工作B處理效能:10筆/秒 慢慢做記憶體跟CPU都不炸 ===方案B,多執行緒=== var result = coll.FindAll(); foreach(var doc in result) { Task Task_CheckData = Task.Factory.StartNew(() => { 工作!(); }); } 結果: 工作A處理效能:900up筆/秒,持續加速 工作B處理效能:15up筆/秒,緩慢加速,資料庫效能都被工作A吃掉了 爐~心~超~載~啦~ 由於一直生出新的Task,但程式又沒有適當的釋放資源,導致記憶體持續上升 吃完所有實體記憶體後執行速度很緩慢,而且也沒有釋放記憶體的情況 ===方案C,多執行緒+Dispose=== var result = coll.FindAll(); foreach(var doc in result) { Task Task_CheckData = Task.Factory.StartNew(() => { 工作!(); }); Task_CheckData.Wait(); Task_CheckData.Dispose(); } 結果: 工作A處理效能:20筆/秒 工作B處理效能:10筆/秒 體悟心靈祥和ˊㄇˋ 已經變成單執行緒的形狀了 ===方案D,多執行緒+ContinueWith,失敗=== var result = coll.FindAll(); foreach(var doc in result) { Task Task_CheckData = Task.Factory.StartNew(() => { 工作!(); }); Task_CheckData.ContinueWith(antecendent => { Task_CheckData_Each.Dispose(); }, TaskScheduler.FromCurrentSynchronizationContext()); } 結果: 程式整個卡住不跑... D方案比C方案早生出來 因為失敗了所以先前沒key ===問題結論=== 大概出在Task的使用上 但餵狗後還是沒發現比較好的解決方案 或許是我關鍵字下錯QQ 請版上先知指教,謝謝 -- ※ 發信站: 批踢踢實業坊(ptt.cc), 來自: 111.82.219.136 ※ 文章網址: https://www.ptt.cc/bbs/C_Sharp/M.1468566664.A.390.html
ssccg: Parallel.ForEach? 07/15 15:13
ssccg: 順便說一下你方案C不叫多執行緒+dispose,你用了Wait()就是 07/15 15:14
ssccg: 把目前的執行緒block到該Task完成後才跑下一個StartNew 07/15 15:16
ssccg: 同時間就是只有單執行緒 07/15 15:16
moumou17: 難怪會被打回原形,還是得等task完成工作 07/15 15:17
moumou17: 感謝提點!請問Parallel.ForEach,建議與Task並用嗎? 07/15 15:18
補充方案D,感謝各位
ssccg: 最上面說錯了,Parallel.ForEach跟Task是一樣的... 07/15 15:34
ssccg: 應該要先看方案B問題是出在哪,應該不是Task沒有釋放資源 07/15 15:35
moumou17: 或許是新增Task的速度大於Task執行完成的速度? 07/15 15:37
moumou17: 但是task不像threadpool有數量的限制... 07/15 15:37
ssccg: 如果是新增速度大於完成速度,可能要調整已經在等待執行的 07/15 15:38
ssccg: Task數量太多,就先暫停新增的動作 07/15 15:38
ssccg: Task底層應該還是用ThreadPool做,所以應該不是Thread太多 07/15 15:38
ssccg: 是排在queue上的Task太多 07/15 15:41
將迴圈改為Parallel.ForEach 工作A處理效能:500up筆/秒,緩慢增加 工作B處理效能:230up筆/秒,緩慢增加 記憶體有增長,但是成長的量不大,且有明顯回收記憶體的現象,持續觀察~ 非常感謝!
cplusplus: 開幾個task持續處理資料不要新開就行了吧~ 你這方式 07/15 15:53
cplusplus: 感覺額外負擔太大了...@@ 07/15 15:53
的確是..之後會試著把Task在迴圈外宣告 然後迴圈內加入新工作 感恩!
enonrick: task 無限增生? 就算你程式夠強吃得下,你還是會卡在IO 07/15 15:54
enonrick: 導致整個程序卡住。最好的作法是把資料分成chunk,每個 07/15 15:55
enonrick: chunk 限制筆數,把chunk丟到 thread 去跑,再視情況調 07/15 15:56
enonrick: 整thread 的數量 07/15 15:57
enonrick: 但就算方案A ,一秒只有20筆,是不是有什麼誤會.. 07/15 15:59
切chunk的方式我有想過,但是資料庫資料蠻不連續的 可能需要用第A筆至第B筆來切 速度的部分,目前是連線去呼叫資料庫,查詢時間略久,每做一次查詢回傳約1xx毫秒 一個工作內會做1~4次查詢 感謝你的建議與問題~~ ※ 編輯: moumou17 (111.82.219.136), 07/15/2016 16:06:00
Litfal: 你的bound在資料庫(I/O bound),用多執行續不太會增加效率 07/15 16:35
Litfal: 一些情況反而會更差,就好像你同時開好幾個執行續去讀同一 07/15 16:36
Litfal: 顆硬碟的資料一樣 07/15 16:36
Litfal: 這種情況要做多執行續優化,不應該用件數(平行)去切割,而 07/15 16:40
Litfal: 是要依工作類型(垂直)去切。 07/15 16:40
cplusplus: 同意樓上,如果你一個工作內容要存取4次DB,應該有其他 07/15 17:12
cplusplus: 方式可以減少DB存取的次數,有機會提高更多效率 07/15 17:13
sorkayi: Parallel.For 速度超快的 越多核心越快 07/16 09:46