【问题标题】:C# Parallel.ForEach - randomly writes a null value to the DataRowC# Parallel.ForEach - 将空值随机写入 DataRow
【发布时间】:2021-01-03 06:08:58
【问题描述】:

为什么下面的代码会随机向DataRow对象写入空值?

ConcurrentDictionary<long, DataRow> dRowDict = new ConcurrentDictionary<long, DataRow>();
foreach (long klucz in data.Keys)
{
    DataRow row = table.NewRow();
    dRowDict.TryAdd(klucz, row);
}
Parallel.ForEach(data.Keys, klucz =>
{
    Dane prz = data[klucz] as Dane;
    DataRow dr = dRowDict[klucz];
    foreach (PropertyDescriptor prop in llProp)
    {
        dr[prop.Name] = prop.GetValue(prz) ?? DBNull.Value;
    }
});
foreach (DataRow dRow in dRowDict.Values)
{
    table.Rows.Add(dRow);
    if (dRow["FILED"] == DBNull.Value)
    {
        MessageBox.Show("ERROR...NULL VALUE"); //why this happen?
    }
}

普通的 for 循环不会导致这个问题 - 为什么,我的错误在哪里?

我的问题是 - 我可以在并行循环中修改任何对象的属性吗?

Parallel.ForEach(myData.AsEnumerable()..., value =>
{
   Object x = new Object(); // or x = dict[key];
   x.A = ...;
   x.B = ...; //Can I do this and is it safe?
}

【问题讨论】:

  • DataTable 不是线程安全的。问题不在于Parallel.ForEach。此外,Parallel.ForEach 用于数据并行,即处理大量数据。它通过对输入进行分区并让专门的工作人员处理分区来做到这一点。您的代码所做的与它完全不同。它只是修改了一些 DataRows。你想做什么,为什么需要并行处理?
  • Parallel.Foreach 将列表视为无序,因此它没有任何顺序运行。
  • @RomanRyzhiy 这并不完全正确 - 分区中的数据是有序的,但不是故意的。
  • 这段代码试图解决什么问题?为什么要提前创建行,而不是在创建时填充它们?

标签: c# foreach parallel-processing datarow


【解决方案1】:

仅当对象的类是线程安全的时,才允许在并行循环内修改对象的属性。换句话说,如果该类专门设计为允许从多个线程同时访问其成员。大多数类不是这样设计的,而是假设一次只能由一个线程访问。

根据我的经验线程-“不安全”被认为是默认值,因此如果您在特定类的documentation 中搜索线程安全部分,但找不到它,你应该假设这个类不是线程安全的。

同时从多个线程访问非线程安全对象会导致“未定义行为”。这是“all kind of nastiness”的一个很好的正式同义词。它包括但不限于随机异常、损坏的状态、丢失的更新、损坏的值、受损的安全性等。这些现象本质上是非确定性的,并且通常是不可重现的。因此,这是一种非常不可取的做法。

ADO.NET classes 仅对多线程读取操作是线程安全的。这意味着多个线程同时读取它们的属性是安全的。但是当一个线程正在修改一个 ADO.NET 对象时,任何其他线程都不应访问该对象,无论是读取还是修改它。

【讨论】:

  • 我以为在Parallel.ForEach循环内部创建的对象只能被一个线程修改。
  • 有多少线程可以修改在 Parallel.ForEach 循环中创建的本地对象?
  • 在并行循环中形成的局部变量是否需要线程安全?
  • @pl.lepko 如果一个对象是在Parallel.ForEach 的 lambda 内创建的,并且它没有分配给外部范围内的变量(或存储在外部集合中),那么它将只能由一个线程访问,因此它不需要是线程安全的。
  • @pl.lepko 在您的情况下,有问题的对象可能是DataTable。尽管根据公共 API,table.NewRow() 创建的行没有附加到它,但它们可能仍然在内部连接到它。所以通过并行更新行,DataTable 的内部状态可能会被多个线程修改,因此会出现问题。
猜你喜欢
  • 2021-06-02
  • 2014-12-21
  • 1970-01-01
  • 2015-10-02
  • 2012-06-16
  • 2021-03-11
  • 1970-01-01
  • 2016-09-06
  • 2011-10-21
相关资源
最近更新 更多