[問題] Task StartNew過多,導致記憶體爆量

作者: moumou17 (第一天)   2016-07-15 15:11:01
===前情提要===
目前在重整資料庫的資料(約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
請版上先知指教,謝謝
作者: ssccg (23)   2016-07-15 15:13:00
Parallel.ForEach?順便說一下你方案C不叫多執行緒+dispose,你用了Wait()就是把目前的執行緒block到該Task完成後才跑下一個StartNew同時間就是只有單執行緒
作者: moumou17 (第一天)   2016-07-15 15:17:00
難怪會被打回原形,還是得等task完成工作感謝提點!請問Parallel.ForEach,建議與Task並用嗎?
作者: ssccg (23)   2016-07-15 15:34:00
最上面說錯了,Parallel.ForEach跟Task是一樣的...應該要先看方案B問題是出在哪,應該不是Task沒有釋放資源
作者: moumou17 (第一天)   2016-07-15 15:37:00
或許是新增Task的速度大於Task執行完成的速度?但是task不像threadpool有數量的限制...
作者: ssccg (23)   2016-07-15 15:38:00
如果是新增速度大於完成速度,可能要調整已經在等待執行的Task數量太多,就先暫停新增的動作Task底層應該還是用ThreadPool做,所以應該不是Thread太多是排在queue上的Task太多
作者: cplusplus (對的人難尋)   2016-07-15 15:53:00
開幾個task持續處理資料不要新開就行了吧~ 你這方式感覺額外負擔太大了...@@
作者: enonrick (EnonRick)   2016-07-15 15:54:00
task 無限增生? 就算你程式夠強吃得下,你還是會卡在IO導致整個程序卡住。最好的作法是把資料分成chunk,每個chunk 限制筆數,把chunk丟到 thread 去跑,再視情況調整thread 的數量但就算方案A ,一秒只有20筆,是不是有什麼誤會..
作者: Litfal (Litfal)   2016-07-15 16:35:00
你的bound在資料庫(I/O bound),用多執行續不太會增加效率一些情況反而會更差,就好像你同時開好幾個執行續去讀同一顆硬碟的資料一樣這種情況要做多執行續優化,不應該用件數(平行)去切割,而是要依工作類型(垂直)去切。
作者: cplusplus (對的人難尋)   2016-07-15 17:12:00
同意樓上,如果你一個工作內容要存取4次DB,應該有其他方式可以減少DB存取的次數,有機會提高更多效率
作者: sorkayi (尋找奶昔)   2016-07-16 09:46:00
Parallel.For 速度超快的 越多核心越快

Links booklink

Contact Us: admin [ a t ] ucptt.com