订购 ConcurrentDictionary。为什么这不起作用?
Ordering a ConcurrentDictionary. Why is this not working?
我们有一个 C# 应用程序,可以在 Excel 文档中的工作表上填充表格。
必须按照行从数据库返回的顺序填充表。
DataFileColData 对象被定义为一个列表并包含结果集行。出于测试目的,我只使用列表中的 [0]。
下面的代码段 #1 不起作用。行顺序未保留,因为最终结果显示的数据乱序,尽管数字本身是按顺序列出的:
if (DataFileColData[0].Count() > 0)
{
ConcurrentDictionary<int, DataRow> theRows = new ConcurrentDictionary<int, DataRow>(9, DataFileColData[0].Count());
Parallel.For(0, DataFileColData[0].Count(), i =>
{
// go through each column
int c = 0;
try
{
foreach (var Col in DataFileColData)
{
var cell = Col[i];
if (cell != null)
{
if (cell.GetType().Name == "JArray") //If Jarray then table compression was used not column compression
{
if (theRows.TryAdd(i, Dt.NewRow()))
theRows[i].ItemArray = JsonConvert.DeserializeObject<object[]>(Col[i].ToString());
}
else
{
if (theRows.TryAdd(i, Dt.NewRow()))
theRows[i][c] = cell;
}
}
c++;
}
} //try
catch (Exception e)
{
throw new Exception("Exception thrown in \"PublicMethods.cs | RenderExcelFile\" while in foreach loop over DataFileColData: " + e.ToString());
}
} //for
); //parallel
//Add the rows to the datatable in their original order
//(might have gotten skewed from the parallel.for loop)
for (int x = 0; x < theRows.Count; x++)
Dt.Rows.Add(theRows[x]);
//Set the name so it appears nicely in the Excel Name Box dropdown instead of "table1", "table2", etc etc.
Dt.TableName = ExcelTableSpec.TableTitle + " " + r.TableID;
}
下面的代码段 #2 确实适用于与保留的每一行相关联的行顺序和数据:
if (DataFileColData[0].Count() > 0)
{
DataRow[] theRows = new DataRow[DataFileColData[0].Count()];
Parallel.For(0, DataFileColData[0].Count(), i =>
{
DataRow Rw = Dt.NewRow();
// go through each column
int c = 0;
try
{
foreach (var Col in DataFileColData)
{
var cell = Col[i];
if (cell != null)
{
if (cell.GetType().Name == "JArray") //If Jarray then table compression was used not column compression
{
lock (theRows)
{
theRows[i] = Dt.NewRow();
theRows[i].ItemArray = JsonConvert.DeserializeObject<object[]>(Col[i].ToString());
}
}
else
{
lock (theRows)
{
theRows[i] = Dt.NewRow();
theRows[i][c] = cell;
}
}
}
c++;
}
} //try
catch (Exception e)
{
throw new Exception("Exception thrown in \"PublicMethods.cs | RenderExcelFile\" while in foreach loop over DataFileColData: " + e.ToString());
}
} //for
); //parallel
//Add the rows to the datatable in their original order
//(might have gotten skewed from the parallel.for loop)
Dt = theRows.CopyToDataTable();
//Set the name so it appears nicely in the Excel Name Box dropdown instead of "table1", "table2", etc etc.
Dt.TableName = ExcelTableSpec.TableTitle + " " + r.TableID;
}
我不明白为什么。我认为不需要锁定机制,因为每个线程都有自己的 "i" 实例,并且 ConcurrentDictionary 应该是线程安全的。
有人可以向我解释为什么代码没有按我认为的方式工作吗?
谢谢!
根据@Enigmativity 下面的评论更新了代码。
MSDN 文档不是很清楚(无论如何对我来说),但似乎确实更新了 DataTable,即使 MSDN 文档没有表明它在执行 NewRow() 方法时确实如此。
下面的新工作代码:
if (DataFileColData[0].Count() > 0)
{
DataRow[] theRows = new DataRow[DataFileColData[0].Count()];
Parallel.For(0, DataFileColData[0].Count(), i =>
//for (int i = 0; i < DataFileColData[0].Count(); i++)
{
lock (Dt)
{
theRows[i] = Dt.NewRow();
}
// go through each column
int c = 0;
try
{
foreach (var Col in DataFileColData)
{
var cell = Col[i];
if (cell != null)
{
if (cell.GetType().Name == "JArray") //If Jarray then table compression was used not column compression
{
theRows[i].ItemArray = JsonConvert.DeserializeObject<object[]>(Col[i].ToString());
}
else
{
theRows[i][c] = cell;
}
}
c += 1;
} //foreach
} //try
catch (Exception e)
{
throw new Exception("Exception thrown in \"PublicMethods.cs | RenderExcelFile\" while in foreach loop over DataFileColData: " + e.ToString());
}
} //for
); //parallel
//Add the rows to the datatable in their original order
//(might have gotten skewed from the parallel.for loop)
Dt = theRows.CopyToDataTable();
//Set the name so it appears nicely in the Excel Name Box dropdown instead of "table1", "table2", etc etc.
Dt.TableName = ExcelTableSpec.TableTitle + " " + r.TableID;
//cleanup
if (theRows != null)
Array.Clear(theRows, 0, theRows.Length);
theRows = null;
} //if (DataFileColData[0].Count() > 0)
请参阅 (MSDN Data Tables) 的文档。
重点是:
Thread Safety
This type is safe for multithreaded read operations. You must
synchronize any write operations.
所以不是 i
ConcurrentDictionary
导致你的问题。
我已经反编译了 NewRow
方法并且有一个对 NewRow(int record)
的调用。此代码清楚地显示了写入操作。
internal DataRow NewRow(int record)
{
if (-1 == record)
record = this.NewRecord(-1);
this.rowBuilder._record = record;
DataRow row = this.NewRowFromBuilder(this.rowBuilder);
this.recordManager[record] = row;
if (this.dataSet != null)
this.DataSet.OnDataRowCreated(row);
return row;
}
我们有一个 C# 应用程序,可以在 Excel 文档中的工作表上填充表格。
必须按照行从数据库返回的顺序填充表。
DataFileColData 对象被定义为一个列表并包含结果集行。出于测试目的,我只使用列表中的 [0]。
下面的代码段 #1 不起作用。行顺序未保留,因为最终结果显示的数据乱序,尽管数字本身是按顺序列出的:
if (DataFileColData[0].Count() > 0)
{
ConcurrentDictionary<int, DataRow> theRows = new ConcurrentDictionary<int, DataRow>(9, DataFileColData[0].Count());
Parallel.For(0, DataFileColData[0].Count(), i =>
{
// go through each column
int c = 0;
try
{
foreach (var Col in DataFileColData)
{
var cell = Col[i];
if (cell != null)
{
if (cell.GetType().Name == "JArray") //If Jarray then table compression was used not column compression
{
if (theRows.TryAdd(i, Dt.NewRow()))
theRows[i].ItemArray = JsonConvert.DeserializeObject<object[]>(Col[i].ToString());
}
else
{
if (theRows.TryAdd(i, Dt.NewRow()))
theRows[i][c] = cell;
}
}
c++;
}
} //try
catch (Exception e)
{
throw new Exception("Exception thrown in \"PublicMethods.cs | RenderExcelFile\" while in foreach loop over DataFileColData: " + e.ToString());
}
} //for
); //parallel
//Add the rows to the datatable in their original order
//(might have gotten skewed from the parallel.for loop)
for (int x = 0; x < theRows.Count; x++)
Dt.Rows.Add(theRows[x]);
//Set the name so it appears nicely in the Excel Name Box dropdown instead of "table1", "table2", etc etc.
Dt.TableName = ExcelTableSpec.TableTitle + " " + r.TableID;
}
下面的代码段 #2 确实适用于与保留的每一行相关联的行顺序和数据:
if (DataFileColData[0].Count() > 0)
{
DataRow[] theRows = new DataRow[DataFileColData[0].Count()];
Parallel.For(0, DataFileColData[0].Count(), i =>
{
DataRow Rw = Dt.NewRow();
// go through each column
int c = 0;
try
{
foreach (var Col in DataFileColData)
{
var cell = Col[i];
if (cell != null)
{
if (cell.GetType().Name == "JArray") //If Jarray then table compression was used not column compression
{
lock (theRows)
{
theRows[i] = Dt.NewRow();
theRows[i].ItemArray = JsonConvert.DeserializeObject<object[]>(Col[i].ToString());
}
}
else
{
lock (theRows)
{
theRows[i] = Dt.NewRow();
theRows[i][c] = cell;
}
}
}
c++;
}
} //try
catch (Exception e)
{
throw new Exception("Exception thrown in \"PublicMethods.cs | RenderExcelFile\" while in foreach loop over DataFileColData: " + e.ToString());
}
} //for
); //parallel
//Add the rows to the datatable in their original order
//(might have gotten skewed from the parallel.for loop)
Dt = theRows.CopyToDataTable();
//Set the name so it appears nicely in the Excel Name Box dropdown instead of "table1", "table2", etc etc.
Dt.TableName = ExcelTableSpec.TableTitle + " " + r.TableID;
}
我不明白为什么。我认为不需要锁定机制,因为每个线程都有自己的 "i" 实例,并且 ConcurrentDictionary 应该是线程安全的。
有人可以向我解释为什么代码没有按我认为的方式工作吗?
谢谢!
根据@Enigmativity 下面的评论更新了代码。
MSDN 文档不是很清楚(无论如何对我来说),但似乎确实更新了 DataTable,即使 MSDN 文档没有表明它在执行 NewRow() 方法时确实如此。
下面的新工作代码:
if (DataFileColData[0].Count() > 0)
{
DataRow[] theRows = new DataRow[DataFileColData[0].Count()];
Parallel.For(0, DataFileColData[0].Count(), i =>
//for (int i = 0; i < DataFileColData[0].Count(); i++)
{
lock (Dt)
{
theRows[i] = Dt.NewRow();
}
// go through each column
int c = 0;
try
{
foreach (var Col in DataFileColData)
{
var cell = Col[i];
if (cell != null)
{
if (cell.GetType().Name == "JArray") //If Jarray then table compression was used not column compression
{
theRows[i].ItemArray = JsonConvert.DeserializeObject<object[]>(Col[i].ToString());
}
else
{
theRows[i][c] = cell;
}
}
c += 1;
} //foreach
} //try
catch (Exception e)
{
throw new Exception("Exception thrown in \"PublicMethods.cs | RenderExcelFile\" while in foreach loop over DataFileColData: " + e.ToString());
}
} //for
); //parallel
//Add the rows to the datatable in their original order
//(might have gotten skewed from the parallel.for loop)
Dt = theRows.CopyToDataTable();
//Set the name so it appears nicely in the Excel Name Box dropdown instead of "table1", "table2", etc etc.
Dt.TableName = ExcelTableSpec.TableTitle + " " + r.TableID;
//cleanup
if (theRows != null)
Array.Clear(theRows, 0, theRows.Length);
theRows = null;
} //if (DataFileColData[0].Count() > 0)
请参阅 (MSDN Data Tables) 的文档。
重点是:
Thread Safety
This type is safe for multithreaded read operations. You must synchronize any write operations.
所以不是 i
ConcurrentDictionary
导致你的问题。
我已经反编译了 NewRow
方法并且有一个对 NewRow(int record)
的调用。此代码清楚地显示了写入操作。
internal DataRow NewRow(int record)
{
if (-1 == record)
record = this.NewRecord(-1);
this.rowBuilder._record = record;
DataRow row = this.NewRowFromBuilder(this.rowBuilder);
this.recordManager[record] = row;
if (this.dataSet != null)
this.DataSet.OnDataRowCreated(row);
return row;
}