Parallel.ForEach和DataTable-DataTable.NewRow()不是线程安全的“读取”线程吗?操作? [英] Parallel.ForEach and DataTable - Isn't DataTable.NewRow() a thread safe "read" operation?

查看:491
本文介绍了Parallel.ForEach和DataTable-DataTable.NewRow()不是线程安全的“读取”线程吗?操作?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在转换现有应用程序以利用多个处理器。我有一些嵌套循环,并且已将最里面的循环转换为Parallel.Foreach循环。在原始应用程序的最内层循环中,代码将调用 DataTable.NewRow()实例化具有适当布局的新DataRow,填充各列并添加使用 DataTable.Add()将DataRow填充到DataTable中。但是由于DataTable仅对于读取操作是线程安全的,因此我将处理过程进行了转换,以将填充的DataRow对象添加到 ConcurrentBag< DataRow> 对象中。然后,一旦Parallel.Foreach循环完成,我将迭代ConcurrentBag并将DataRow对象添加到DataTable中。看起来像这样...

I'm converting an existing application to take advantage of multiple processors. I have some nested loops, and I've converted the inner-most loop into a Parallel.Foreach loop. In the original application, inside the inner-most loop, the code would call DataTable.NewRow() to instantiate a new DataRow of the appropriate layout, populate the columns and add the populated DataRow into the DataTable with DataTable.Add(). But since DataTable is only thread-safe for read operations, I have converted the processing to add populated DataRow objects into a ConcurrentBag<DataRow> object. Then, once the Parallel.Foreach looping finishes, I iterate the ConcurrentBag and add the DataRow objects into the DataTable. It looks something like this...

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对象吗?

So when this runs, I'm seeing an, "Index was outside the bounds of the array." exception on the call to MyDataTable.NewRow(). But wouldn't NewRow() be a thread-safe, Read operation? Sure, it instantiates a new DataRow object, and that's not a read. But it doesn't need to modify the DataTable object, does it?

这可能会有所帮助...当我查看异常时,上面的前两项我的调用堆栈是...在System.Data.DataTable.NewRow(Int32 record)


This might help a bit... When I look at the exception, the top two items on my call stack are...

   at System.Data.DataTable.NewRow(Int32 record)
   at System.Data.DataTable.NewRow()
   at ...

我看到 NewRow()调用必须为私有的 NewRow(int32)方法。所以也许就是这个问题。但我不确定如何解决。如果需要的话,我可以创建它,而不必从我的Parallel.Foreach循环中实例化DataRow对象,只需实例化一个看起来很像DataTable的自定义对象,一旦循环退出,就实例化实际的DataRows并将其添加到数据表。但这不算优雅,而是实例化不必要的对象。我的目标是提高性能,从而似乎适得其反。

And I see that NewRow() calls what must be a private NewRow(int32) method. So maybe that's the issue. But I'm not sure how to solve it. If I have to, I can get creating and instead of instantiating the DataRow object from within my Parallel.Foreach loop, just instantiate a custom object that looks a lot like my DataTable and once the loop exits, instantiate the actual DataRows and add them to the DataTable. But that is less than elegant, and instantiates "unnecessary" objects. And my goal is to improve performance, so that seems counterproductive.

感谢您的协助。

推荐答案

否, NewRow 不是读取操作,也不是线程安全的。

No, NewRow is not a "read" operation and is not thread safe.

代替使用 NewRow 并填充行,您只需将值放在对象的数组或列表中即可。然后,当您收集了所有数据后,可以将其全部添加到 DataTable

Instead of using NewRow and populating the row you could just place your values in an array or list of object. Then when you've collected all of your data you can add it all to the DataTable.

var newRow = table.NewRow();
newRow.ItemArray = values; // array of values
table.Rows.Add(newRow);

这样,您可以并行创建数据,而无需将其添加到 DataTable

That way the you can parallelize the creation of your data without running into issues when you add it to the DataTable.

查看 DataTable 的源代码 code>:

Looking at the source code for DataTable:

它包含多个字段:

private readonly DataRowBuilder rowBuilder;
internal readonly RecordManager recordManager;

NewRow()调用 NewRow(-1) NewRow(int)修改这些字段的状态:

NewRow() calls NewRow(-1), and NewRow(int) modifies the state of those fields:

    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 的状态

...and there's much more that I haven't followed. But what's clear is that NewRow() does more than just return a new row - it modifies the state of the DataTable instance all over the place.

文档从未说过它是线程安全的,但是我猜想是因为您仍然必须将行添加到表中, NewRow 没有修改 DataTable 。但是我会错了,而且绝对不是线程安全的。

The documentation never said it was thread safe, but I would have guessed that because you still have to add the row to the table, NewRow didn't modify the DataTable. But I would be wrong, and it's definitely not thread safe.

另一个标志位于 NewRow

Another flag is in the documentation for NewRow


创建DataRow之后,可以通过DataTable对象的Rows属性将其添加到DataRowCollection中。当您使用NewRow创建新行时,必须在调用Clear之前将这些行添加到数据表中或从数据表中删除。

After creating a DataRow, you can add it to the DataRowCollection, through the DataTable object's Rows property. When you use NewRow to create new rows, the rows must be added to or deleted from the data table before you call Clear.

没有说如果您调用 Clear()而没有添加或删除使用 NewRow()创建的行会发生什么。有例外吗我会死吗?所以我尝试了。我仍然在这里,但是调用 Clear()将每行中的所有值替换为 DBNull.Value ,进一步强调,只有将行添加到 DataTable 之前,这些行才可以消失。它们是该州的一部分。

It doesn't say what will happen if you call Clear() without adding or deleting a row created with NewRow(). An exception? Will I die? So I tried. I'm still here, but calling Clear() replaced all of the values in each row with DBNull.Value, further underscoring that the rows are not "disembodied" until they are added to the DataTable. They are part of its state.

这篇关于Parallel.ForEach和DataTable-DataTable.NewRow()不是线程安全的“读取”线程吗?操作?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆