我正在转换现有应用程序以利用多个处理器。我有一些嵌套循环,并且已将最里面的循环转换为Parallel.Foreach循环。在原始应用程序的最内层循环中,代码将调用DataTable.NewRow()
以实例化具有适当布局的新DataRow,填充列,然后使用DataTable.Add()
将填充的DataRow添加到DataTable中。但是由于DataTable仅对于读取操作是线程安全的,因此我将处理过程进行了转换,以将填充的DataRow对象添加到ConcurrentBag<DataRow>
对象中。然后,一旦Parallel.Foreach循环完成,我将迭代ConcurrentBag并将DataRow对象添加到DataTable中。看起来像这样...
DataTable MyDataTable = new DataTable()
// Add columns to the data table
For(int OuterLoop = 1; OuterLoop < MaxValue; OuterLoop++)
{
//Do Stuff...
ConcurrentBag<DataRow> CB = new ConcurrentBag<DataRow>();
Parallel.Foreach(MyCollectionToEnumerate, x =>
{
//Do Stuff
DataRow dr = MyDataTable.NewRow();
// Populate dr...
CB.Add(dr);
{);
ForEach(DataRow d in CB)
MyDataTable.Add(d);
}
所以当它运行时,我看到一个“索引在数组的边界之外”。呼叫MyDataTable.NewRow()
的异常。但是NewRow()不会是线程安全的读取操作吗?当然,它实例化了一个新的DataRow对象,但这不是读取。但这不需要修改DataTable对象,对吗?
这可能会有所帮助...当我查看异常时,调用堆栈上的前两项是...
at System.Data.DataTable.NewRow(Int32 record)
at System.Data.DataTable.NewRow()
at ...
我看到NewRow()
调用了必须是私有NewRow(int32)
方法。所以也许就是这个问题。但我不确定如何解决。如果需要的话,我可以创建它,而不必从我的Parallel.Foreach循环中实例化DataRow对象,只需实例化一个看起来很像我的DataTable的自定义对象,一旦循环退出,就实例化实际的DataRows并将其添加到数据表。但这不算优雅,而是实例化“不必要的”对象。而我的目标是提高性能,从而适得其反。
感谢您的帮助。
答案 0 :(得分:2)
否,NewRow
不是“读取”操作,也不是线程安全的。
代替使用NewRow
并填充行,您只需将值放在object
的数组或列表中即可。然后,当您收集完所有数据后,可以将其全部添加到DataTable
。
var newRow = table.NewRow();
newRow.ItemArray = values; // array of values
table.Rows.Add(newRow);
这样,当您将数据添加到DataTable
时,您可以并行创建数据,而不会出现问题。
查看DataTable
的{{3}}:
它包含多个字段:
private readonly DataRowBuilder rowBuilder;
internal readonly RecordManager recordManager;
NewRow()
调用NewRow(-1)
,而NewRow(int)
修改这些字段的状态:
internal DataRow NewRow(int record) {
if (-1 == record) {
record = NewRecord(-1);
}
rowBuilder._record = record; // here
DataRow row = NewRowFromBuilder( rowBuilder );
recordManager[record] = row; // here
if (dataSet != null)
DataSet.OnDataRowCreated( row );
return row;
}
...还有更多我没有关注的地方。但是很明显,NewRow()
所做的不仅仅是返回新行-它在整个地方都修改了DataTable
实例的状态。
文档从未说过它是线程安全的,但是我猜想是因为您仍然必须将行添加到表中,NewRow
并没有修改DataTable
。但是我会错的,而且绝对不是线程安全的。
创建DataRow后,可以通过DataTable对象的Rows属性将其添加到DataRowCollection。使用NewRow创建新行时,必须在调用Clear之前将这些行添加到数据表中或从数据表中删除。
如果您在不添加或删除用Clear()
创建的行的情况下调用NewRow()
,并没有说什么。有例外吗我会死吗?所以我尝试了。我仍然在这里,但是调用Clear()
会将每行中的所有值替换为DBNull.Value
,进一步强调了只有在将行添加到DataTable
之前,这些行才是“无形的”。它们是其状态的一部分。