我正在尝试向 DataTable 添加一些并行信息,但如果循环太长,它会卡住或只花费大量时间,比通常的 for 循环更多的时间,这是我的并行代码。对于循环:
Parallel.For(1, linii.Length, index =>
{
DataRow drRow = dtResult.NewRow();
alResult = CSVParser(linii[index], txtDelimiter, txtQualifier);
for (int i = 0; i < alResult.Count; i++)
{
drRow[i] = alResult[i];
}
dtResult.Rows.Add(drRow);
}
);
怎么了?这个 Parallel.For 循环比普通循环花费更多时间,有什么问题吗?
谢谢!
最佳答案
你不能改变来自 2 个不同线程的 DataTable
;它会出错。 DataTable
不尝试 是线程安全的。所以:不要那样做。只需从一个线程执行此操作。您很可能受到 IO 的限制,因此您应该只在单个线程上将其作为流来执行。看起来您正在处理文本数据。您似乎有一个 string[]
行,也许是 File.ReadAllLines()
?嗯,这里很糟糕:
- 它强制所有内容加载到内存中
- 您必须等待将其全部加载到内存中
- CSV 是一种多行格式;不保证 1 行 == 1 行
您应该做的是使用代码项目中的 CsvReader 之类的东西,但即使您只想一次使用一行,也可以使用 StreamReader:
using(var file = File.OpenText(path)) {
string line;
while((line = file.ReadLine()) != null) {
// process this line
alResult = CSVParser(line, txtDelimiter, txtQualifier);
for (int i = 0; i < alResult.Count; i++)
{
drRow[i] = alResult[i];
}
dtResult.Rows.Add(drRow);
}
}
使用 Parallel
这不会更快,所以我没有尝试这样做。 IO 是你的瓶颈。锁定是一种选择,但它不会对您有很大帮助。
顺便说一句,我注意到 alResult
没有在循环内声明。这意味着在您的原始代码中,alResult
是一个捕获的变量,它在所有 循环迭代之间共享 - 这意味着你已经可怕地覆盖了每一行。
编辑:说明为什么 Parallel
与从文件中读取 1,000,000 行无关:
方法一:使用ReadAllLines
加载行,然后使用Parallel
进行处理;这会花费 [固定时间] 用于物理文件 IO,然后我们并行化。 CPU 的工作量很少,我们基本上已经花费了[固定时间]。但是,我们增加了很多线程开销和内存开销,甚至在所有文件加载完成之前我们都无法开始。
方法 2:使用流式 API;逐行阅读每一行 - 处理每一行并添加它。这里的成本基本上又是:[固定时间]实际IO带宽来加载文件。但;我们现在没有线程开销,没有同步冲突,没有大内存要分配,我们马上开始填充表。
方法 3:如果您真的想要,第三种方法是读取器/写入器队列,使用一个专用线程处理文件 IO 并将行排入队列,第二个线程执行 数据表
。坦率地说,它有很多移动部件,第二个线程将花费 95% 的时间等待文件中的数据;坚持方法 2!
关于c# - Parallel.For 循环卡住,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11883897/