订购ConcurrentDictionary。为什么这不起作用?

时间:2015-09-16 00:33:45

标签: c# concurrency parallel-processing concurrentdictionary parallel.for

我们有一个C#应用程序,用于在Excel文档中的工作表上填充表格。

必须按照从数据库返回行的顺序填充表。

对象DataFileColData定义为List并包含结果集行。出于测试目的,我只使用List的[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)

1 个答案:

答案 0 :(得分:2)

请参阅(MSDN Data Tables)的文档。

关键点是:

  

线程安全

     

此类型对于多线程读取操作是安全的。你必须   同步任何写操作。

因此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;
}
相关问题