Parallel.ForEach 没有或很少发生
Parallel.ForEach nothing or very little is happening
我正在尝试读取 excel 文档并将其写为 csv。
我已经想出了如何通过几种略有不同的方式来做到这一点,但速度非常慢。
这就是我所拥有的正在工作的东西,它在大约 1 分 36 秒内 运行 通过 2 个方舟,每个方舟有 16384 行和 5 列数据
public void ToCSV(Stream excelStream, int i)
{
// IExcelDataReader excelReader = null;
using (var excelReader = ExcelReaderFactory.CreateReader(excelStream))
{
System.Data.DataSet excelsheets = excelReader.AsDataSet();
foreach (DataTable aSheet in excelsheets.Tables)
{
string strCSVData = "";
string sheetName = aSheet.TableName;
foreach (DataRow row in aSheet.Rows)
{
foreach (var column in row.ItemArray)
{
strCSVData += column.ToString().Replace(",", ",") + ",";
}
strCSVData += "\n";
}
string strOutputFileName = Directory.GetCurrentDirectory() + sheetName + i.ToString() + ".csv";
StreamWriter csvFile = new StreamWriter(strOutputFileName, false);
csvFile.Write(strCSVData);
csvFile.Close();
}
}
}
现在我正在尝试加快速度。我使用普通的 for 循环稍微快一点,但在大约 1 分 33 秒时没有什么特别的。
所以我考虑改用 Parallel.foreach 怎么样。然而,这导致只有三分之一的数据被写入,或者当前 none。
我就是这样修改上面的方法的。
public void ToCSVParallel(Stream excelStream, int i)
{
// IExcelDataReader excelReader = null;
using (var excelReader = ExcelReaderFactory.CreateReader(excelStream))
{
System.Data.DataSet excelsheets = excelReader.AsDataSet();
for (int sheet = 0; sheet < excelsheets.Tables.Count; sheet++)
{
DataTable aSheet = excelsheets.Tables[sheet];
List<string> strCSVData = new List<string>();
string sheetName = aSheet.TableName;
IEnumerable<DataRow> dataSheet = aSheet.AsEnumerable();
Parallel.ForEach<DataRow>(dataSheet, row =>
{
string strRow = "";
for (int column = 0; column < row.ItemArray.Count(); column++)
{
strRow = row[column].ToString().Replace(",", ",") + ",";
}
strRow += "\n";
strCSVData.Append(strRow);
});
string strOutputFileName = Directory.GetCurrentDirectory() + sheetName + i.ToString() + ".csv";
//StreamWriter csvFile = new StreamWriter(strOutputFileName, false);
System.IO.File.WriteAllLines(strOutputFileName, strCSVData);
// csvFile.Write(strCSVData);
//csvFile.Close();
}
}
}
现在我不知道我做错了什么但我很确定我一定是误解了我如何使用 parallel.foreach 但是我做错了什么?
或者是否有 better/smarter/simpler 方法来加快我的方法?
编辑:
根据您的所有建议,我做出了以下更改。
public void ToCSVParallel(Stream excelStream, int i)
{
using (var excelReader = ExcelReaderFactory.CreateReader(excelStream))
{
System.Data.DataSet excelsheets = excelReader.AsDataSet();
for (int sheet = 0; sheet < excelsheets.Tables.Count; sheet++)
{
DataTable aSheet = excelsheets.Tables[sheet];
ConcurrentBag<string> strCSVData = new ConcurrentBag<string>();
string sheetName = aSheet.TableName;
IEnumerable<DataRow> dataSheet = aSheet.AsEnumerable();
Parallel.ForEach<DataRow>(dataSheet, row =>
{
StringBuilder strRow = new StringBuilder();
for (int column = 0; column < row.ItemArray.Count(); column++)
{
strRow.Append(row[column].ToString().Replace(",", ",") + ",");
}
strCSVData.Add(strRow.ToString());
});
string strOutputFileName = Directory.GetCurrentDirectory() + sheetName + i.ToString() + ".csv";
System.IO.File.WriteAllLines(strOutputFileName, strCSVData);
}
}
}
但是根据@Magnus 的建议,我也将原来的方法更改为:
public void ToCSV(Stream excelStream, int i)
{
using (var excelReader = ExcelReaderFactory.CreateReader(excelStream))
{
System.Data.DataSet excelsheets = excelReader.AsDataSet();
foreach (DataTable aSheet in excelsheets.Tables)
{
string sheetName = aSheet.TableName;
string strOutputFileName = Directory.GetCurrentDirectory() + sheetName + i.ToString() + ".csv";
using (StreamWriter csvFile = new StreamWriter(strOutputFileName, false))
{
foreach (DataRow row in aSheet.Rows)
{
foreach (var column in row.ItemArray)
{
csvFile.Write(column.ToString().Replace(",", ",") + ",");
}
csvFile.WriteLine();
}
}
}
}
}
结果令我震惊。
并行比修改后的 Foreach 循环平均慢 1000 毫秒。
不过,我想使该方法更快的想法现在已经在可接受的范围内。
并行平均需要大约 8800 毫秒。
foreach 循环平均需要 7600 毫秒。
这两个都在 2 个方舟上,每个方舟有 16384 行和 5 列数据
你的代码有几个问题。
strCSVData.Append(strRow)
实际上并没有向列表中添加任何内容,它 returns 一个附加了项目的新枚举。
- 如果你真的做了
Add
那也行不通,因为 List 不是线程安全的。
- 并行不会按顺序处理项目。 (必然)
我认为您的原始(非并行循环)的主要问题是通过串联构建 strCSVData
。由于字符串是不可变的,因此每次都必须创建一个新字符串,而且它越大,速度就越慢。我建议您在循环之前打开 StreamWriter
并直接写入那个。
...
StreamWriter csvFile = new StreamWriter(strOutputFileName, false);
for (int sheet = 0; sheet < excelsheets.Tables.Count; sheet++)
{
...
foreach (DataRow row in aSheet.Rows)
{
foreach (var column in row.ItemArray)
{
csvFile.WriteLine(column.ToString().Replace(",", ",") + ",");
}
}
...
}
我正在尝试读取 excel 文档并将其写为 csv。
我已经想出了如何通过几种略有不同的方式来做到这一点,但速度非常慢。
这就是我所拥有的正在工作的东西,它在大约 1 分 36 秒内 运行 通过 2 个方舟,每个方舟有 16384 行和 5 列数据
public void ToCSV(Stream excelStream, int i)
{
// IExcelDataReader excelReader = null;
using (var excelReader = ExcelReaderFactory.CreateReader(excelStream))
{
System.Data.DataSet excelsheets = excelReader.AsDataSet();
foreach (DataTable aSheet in excelsheets.Tables)
{
string strCSVData = "";
string sheetName = aSheet.TableName;
foreach (DataRow row in aSheet.Rows)
{
foreach (var column in row.ItemArray)
{
strCSVData += column.ToString().Replace(",", ",") + ",";
}
strCSVData += "\n";
}
string strOutputFileName = Directory.GetCurrentDirectory() + sheetName + i.ToString() + ".csv";
StreamWriter csvFile = new StreamWriter(strOutputFileName, false);
csvFile.Write(strCSVData);
csvFile.Close();
}
}
}
现在我正在尝试加快速度。我使用普通的 for 循环稍微快一点,但在大约 1 分 33 秒时没有什么特别的。
所以我考虑改用 Parallel.foreach 怎么样。然而,这导致只有三分之一的数据被写入,或者当前 none。
我就是这样修改上面的方法的。
public void ToCSVParallel(Stream excelStream, int i)
{
// IExcelDataReader excelReader = null;
using (var excelReader = ExcelReaderFactory.CreateReader(excelStream))
{
System.Data.DataSet excelsheets = excelReader.AsDataSet();
for (int sheet = 0; sheet < excelsheets.Tables.Count; sheet++)
{
DataTable aSheet = excelsheets.Tables[sheet];
List<string> strCSVData = new List<string>();
string sheetName = aSheet.TableName;
IEnumerable<DataRow> dataSheet = aSheet.AsEnumerable();
Parallel.ForEach<DataRow>(dataSheet, row =>
{
string strRow = "";
for (int column = 0; column < row.ItemArray.Count(); column++)
{
strRow = row[column].ToString().Replace(",", ",") + ",";
}
strRow += "\n";
strCSVData.Append(strRow);
});
string strOutputFileName = Directory.GetCurrentDirectory() + sheetName + i.ToString() + ".csv";
//StreamWriter csvFile = new StreamWriter(strOutputFileName, false);
System.IO.File.WriteAllLines(strOutputFileName, strCSVData);
// csvFile.Write(strCSVData);
//csvFile.Close();
}
}
}
现在我不知道我做错了什么但我很确定我一定是误解了我如何使用 parallel.foreach 但是我做错了什么?
或者是否有 better/smarter/simpler 方法来加快我的方法?
编辑:
根据您的所有建议,我做出了以下更改。
public void ToCSVParallel(Stream excelStream, int i)
{
using (var excelReader = ExcelReaderFactory.CreateReader(excelStream))
{
System.Data.DataSet excelsheets = excelReader.AsDataSet();
for (int sheet = 0; sheet < excelsheets.Tables.Count; sheet++)
{
DataTable aSheet = excelsheets.Tables[sheet];
ConcurrentBag<string> strCSVData = new ConcurrentBag<string>();
string sheetName = aSheet.TableName;
IEnumerable<DataRow> dataSheet = aSheet.AsEnumerable();
Parallel.ForEach<DataRow>(dataSheet, row =>
{
StringBuilder strRow = new StringBuilder();
for (int column = 0; column < row.ItemArray.Count(); column++)
{
strRow.Append(row[column].ToString().Replace(",", ",") + ",");
}
strCSVData.Add(strRow.ToString());
});
string strOutputFileName = Directory.GetCurrentDirectory() + sheetName + i.ToString() + ".csv";
System.IO.File.WriteAllLines(strOutputFileName, strCSVData);
}
}
}
但是根据@Magnus 的建议,我也将原来的方法更改为:
public void ToCSV(Stream excelStream, int i)
{
using (var excelReader = ExcelReaderFactory.CreateReader(excelStream))
{
System.Data.DataSet excelsheets = excelReader.AsDataSet();
foreach (DataTable aSheet in excelsheets.Tables)
{
string sheetName = aSheet.TableName;
string strOutputFileName = Directory.GetCurrentDirectory() + sheetName + i.ToString() + ".csv";
using (StreamWriter csvFile = new StreamWriter(strOutputFileName, false))
{
foreach (DataRow row in aSheet.Rows)
{
foreach (var column in row.ItemArray)
{
csvFile.Write(column.ToString().Replace(",", ",") + ",");
}
csvFile.WriteLine();
}
}
}
}
}
结果令我震惊。
并行比修改后的 Foreach 循环平均慢 1000 毫秒。
不过,我想使该方法更快的想法现在已经在可接受的范围内。 并行平均需要大约 8800 毫秒。 foreach 循环平均需要 7600 毫秒。 这两个都在 2 个方舟上,每个方舟有 16384 行和 5 列数据
你的代码有几个问题。
strCSVData.Append(strRow)
实际上并没有向列表中添加任何内容,它 returns 一个附加了项目的新枚举。- 如果你真的做了
Add
那也行不通,因为 List 不是线程安全的。 - 并行不会按顺序处理项目。 (必然)
我认为您的原始(非并行循环)的主要问题是通过串联构建 strCSVData
。由于字符串是不可变的,因此每次都必须创建一个新字符串,而且它越大,速度就越慢。我建议您在循环之前打开 StreamWriter
并直接写入那个。
...
StreamWriter csvFile = new StreamWriter(strOutputFileName, false);
for (int sheet = 0; sheet < excelsheets.Tables.Count; sheet++)
{
...
foreach (DataRow row in aSheet.Rows)
{
foreach (var column in row.ItemArray)
{
csvFile.WriteLine(column.ToString().Replace(",", ",") + ",");
}
}
...
}