作者moumou17 (第一天)
看板C_Sharp
标题[问题] Task StartNew过多,导致记忆体爆量
时间Fri Jul 15 15:11:01 2016
===前情提要===
目前在重整资料库的资料(约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://webptt.com/cn.aspx?n=bbs/C_Sharp/M.1468566664.A.390.html
1F:→ ssccg: Parallel.ForEach? 07/15 15:13
2F:→ ssccg: 顺便说一下你方案C不叫多执行绪+dispose,你用了Wait()就是 07/15 15:14
3F:→ ssccg: 把目前的执行绪block到该Task完成後才跑下一个StartNew 07/15 15:16
4F:→ ssccg: 同时间就是只有单执行绪 07/15 15:16
5F:→ moumou17: 难怪会被打回原形,还是得等task完成工作 07/15 15:17
6F:→ moumou17: 感谢提点!请问Parallel.ForEach,建议与Task并用吗? 07/15 15:18
补充方案D,感谢各位
7F:→ ssccg: 最上面说错了,Parallel.ForEach跟Task是一样的... 07/15 15:34
8F:→ ssccg: 应该要先看方案B问题是出在哪,应该不是Task没有释放资源 07/15 15:35
9F:→ moumou17: 或许是新增Task的速度大於Task执行完成的速度? 07/15 15:37
10F:→ moumou17: 但是task不像threadpool有数量的限制... 07/15 15:37
11F:→ ssccg: 如果是新增速度大於完成速度,可能要调整已经在等待执行的 07/15 15:38
12F:→ ssccg: Task数量太多,就先暂停新增的动作 07/15 15:38
13F:→ ssccg: Task底层应该还是用ThreadPool做,所以应该不是Thread太多 07/15 15:38
14F:→ ssccg: 是排在queue上的Task太多 07/15 15:41
将回圈改为Parallel.ForEach
工作A处理效能:500up笔/秒,缓慢增加
工作B处理效能:230up笔/秒,缓慢增加
记忆体有增长,但是成长的量不大,且有明显回收记忆体的现象,持续观察~
非常感谢!
15F:推 cplusplus: 开几个task持续处理资料不要新开就行了吧~ 你这方式 07/15 15:53
16F:→ cplusplus: 感觉额外负担太大了...@@ 07/15 15:53
的确是..之後会试着把Task在回圈外宣告
然後回圈内加入新工作
感恩!
17F:→ enonrick: task 无限增生? 就算你程式够强吃得下,你还是会卡在IO 07/15 15:54
18F:→ enonrick: 导致整个程序卡住。最好的作法是把资料分成chunk,每个 07/15 15:55
19F:→ enonrick: chunk 限制笔数,把chunk丢到 thread 去跑,再视情况调 07/15 15:56
20F:→ enonrick: 整thread 的数量 07/15 15:57
21F:→ enonrick: 但就算方案A ,一秒只有20笔,是不是有什麽误会.. 07/15 15:59
切chunk的方式我有想过,但是资料库资料蛮不连续的
可能需要用第A笔至第B笔来切
速度的部分,目前是连线去呼叫资料库,查询时间略久,每做一次查询回传约1xx毫秒
一个工作内会做1~4次查询
感谢你的建议与问题~~
※ 编辑: moumou17 (111.82.219.136), 07/15/2016 16:06:00
22F:→ Litfal: 你的bound在资料库(I/O bound),用多执行续不太会增加效率 07/15 16:35
23F:→ Litfal: 一些情况反而会更差,就好像你同时开好几个执行续去读同一 07/15 16:36
24F:→ Litfal: 颗硬碟的资料一样 07/15 16:36
25F:→ Litfal: 这种情况要做多执行续优化,不应该用件数(平行)去切割,而 07/15 16:40
26F:→ Litfal: 是要依工作类型(垂直)去切。 07/15 16:40
27F:→ cplusplus: 同意楼上,如果你一个工作内容要存取4次DB,应该有其他 07/15 17:12
28F:→ cplusplus: 方式可以减少DB存取的次数,有机会提高更多效率 07/15 17:13
29F:推 sorkayi: Parallel.For 速度超快的 越多核心越快 07/16 09:46