使用.net 的tpl并行计算,主要是从Excel表中读取21万条数据,对满足要求的赋值到Hashtable中,采用并行计算,为什么每次读到4万多条时,提示“没有足够的内存执行该程序”?而在串行计算时,就不会存在这个问题,串行计算大概用1个小时完成。本人笔记本电脑T400双核CPU,内存3G。代码如下,请教高手指教是哪里出了问题:
//ws为Excel表单
int sumlength = 210000;
Hashtable table1 = new Hashtable();
Hashtable table2 = new Hashtable();
Parallel.For(1,sumlength, i=>
{
//对每一行,读取Excel表单ws第2列、第3列的数据
string str1 = ((Excel.Range)ws.Cells[i, 2]).Text.ToString().Trim();
string str2 = ((Excel.Range)ws.Cells[i, 3]).Text.ToString().Trim();
//如果满足规则,哈希表中增加数据
if ((!String.IsNullOrEmpty(str1)) && str1.Contains("在"))
{
table1.Add(i.ToString(), "1");
}
if ((!String.IsNullOrEmpty(str2)) && (!String.Equals(str2, "无")))
{
table2.Add(i.ToString(), "1");
}
}
);
//ws为Excel表单
int sumlength = 210000;
Hashtable table1 = new Hashtable();
Hashtable table2 = new Hashtable();
Parallel.For(1,sumlength, i=>
{
//对每一行,读取Excel表单ws第2列、第3列的数据
string str1 = ((Excel.Range)ws.Cells[i, 2]).Text.ToString().Trim();
string str2 = ((Excel.Range)ws.Cells[i, 3]).Text.ToString().Trim();
//如果满足规则,哈希表中增加数据
if ((!String.IsNullOrEmpty(str1)) && str1.Contains("在"))
{
table1.Add(i.ToString(), "1");
}
if ((!String.IsNullOrEmpty(str2)) && (!String.Equals(str2, "无")))
{
table2.Add(i.ToString(), "1");
}
}
);
{
for (int j = 0; j < sumlength / 10; j++)
{
int i = j * 10 + x;
//对每一行,读取Excel表单ws第2列、第3列的数据
string str1 = ((Excel.Range)ws.Cells[i, 2]).Text.ToString().Trim();
string str2 = ((Excel.Range)ws.Cells[i, 3]).Text.ToString().Trim();
//如果满足规则,哈希表中增加数据
if ((!String.IsNullOrEmpty(str1)) && str1.Contains("在"))
{
table1.Add(i.ToString(), "1");
}
if ((!String.IsNullOrEmpty(str2)) && (!String.Equals(str2, "无")))
{
table2.Add(i.ToString(), "1");
}
}
}
这样可能快一些。但是就你的代码来说,不好说。Excel的调用可能会被上锁。
而在执行Parallel.For时,TPL不会这样做,需要人工提前划分好?
当时也考虑到线程之间Hashtable存储时冲突的问题,但是加锁的话,计算速度上不去了,除了哈希表,还有其他好的数据存储方式么?或者为每个任务建1个独立的哈希表,最后再合并到一起这种方式?
parallel.foreach (Partitioner.Create(1, 任务数量n , 任务数n/处理器数+1),...)
最大并行数, 也可以指定, parallelOption.MaxDegreeOfPara***
任务调度器,你这个就不需要了
你的多线程的算法和数据结构, 也不行.
对处理器缓存Cache也极不友好.慢5倍,都是可能的. 读Excel几行,又去判断字符串,hash, 大量的缓存/指令分支预测失效.[你的代码, 从CPU的缓存,到硬盘的缓存, 所有的一切Cache, 都搞得一塌糊涂.]应该 AAA读取数据. BBB处理数据. 并行的生产者,消费者
还有,这四处充满了 代价很高的同步,也是错误的.另外,复杂计算,参照MapReduce 的思路
map = xxx.asparallel().ToLookUp
reduce = map.asParallel.xxx.xxx....
..还有, toString, 又 trim, 大量的string对象生灭,性能很差的.
------------------------------就你的例子
很明显, A ==> data ==>B , 有很多IProducerConsumerCollection的可选. 当然,针对这个例子,有更高性能的,自己写的版本.A: 多线程,预先分块, 每个线程块内线性顺序读取 excel 文件.
Enqueue ==> 并行无锁的 ConcurrentQueue或者其它的
(为什么,读取硬盘IO要多线程? 现代机械盘支持NCQ以及SSD,在多线程队列深度更大的情况下,性能更高.有大量数据测试. 关键词: 队列深度 SSD 评测)B:TryDequeue, 各线程,维护自己的 数据,C: 归并B产生的数据
你的 内存不足, 原因是因为 String . 21万数据, 可能临时的string有三四十万.或者更多
这个, 是泛泛而谈, 指的是常见的 并行编程的错误. 可能你的代码,没有这个错误,没仔细看你可以参照 MSDN , ConcurrentQueue ,或者其它并行容器里面的, 例子.你的题目,可以典型的 生产者,消费者. 并且,通过类似mapReduce的思路, 把同步降到很低最好呢, 还是系统性地学习, 按照科班的培养计划, 本科,硕士,博士等这些的培养套路.
以后,可能不要excel,换成csv格式的文件, 都可以,只需换写S01高级一点, S02 可以使用类似mapReduce的思路.可以用一个"调度器",根据实际的数据的特性把不同数据分布在不同的线程, 各个线程,独立处理数据,无需同步. 全部完成后,再归并
还可以进一步重构, 玩出很多花样.