【问题标题】:Modifying elements of IEnumerable<T> inside a Parallel.ForEach [closed]在 Parallel.ForEach 中修改 IEnumerable<T> 的元素 [关闭]
【发布时间】:2016-01-05 14:56:37
【问题描述】:

我正在修改并行 foreach 中的元素列表,例如:

    var items = ...; // array to process
    for(;;){
            Parallel.ForEach(items, po, (it) =>
            {
                    it.Date = DateTime.Now; // Modify it with some logic.
            }
    }

我的案例不涉及任何竞速条件,并且这个类似的问题与我的案例无关(因为我对线程安全不感兴趣):

Parallel.ForEach on List<Object> Thread Safety

上述代码中的DateDateTime? 类型,最初是null。我从来没有在我的代码中把它变成null,我只把它设置为DateTime.Now。 问题是Parallel.ForEach 似乎提供items 的副本而不是真实实例,因此无法更新它们。我理解这一点,因为对于之前设置的项目,我得到了 nullDate 值。

这是预期的行为吗?尽管在将数据提供给线程之前复制数据听起来有点合乎逻辑,但我找不到任何文档。

我问,因为在上面发布的问题链接中,没有人说任何关于这个(更新并行 foreach 中的对象)是不可能/可靠的。

是否有任何好看的代码作为解决此问题的方法(以便我可以在循环数组时更新项目)?

更新:

在我的情况下,T 是这样的:

public class GroupSettings
    {
        public GroupSettings(int groupId, string email)
        {
            GroupId = groupId;
            Email = email;
        }
        public int GroupId { get; protected set; }
        public string Email { get; protected set; }
        public DateTime? Date { get; set; }
}

我什至尝试将 Date 放在内部类中(以防万一副本中的对象引用相同),但行为没有改变。

【问题讨论】:

  • 在这种情况下,T 是什么?听起来它可能是一个可变结构...如果不是,请发布一个简短但完整的示例来说明问题。
  • 你的items是什么类型的?它是值类型还是引用类型?如果它是引用类型(类)而不是值类型(结构),您的代码应该可以工作。
  • 我已经更新了上面关于T 类型的问题。 :)
  • 为什么你的Parallel.ForEach 在一个无限循环中(for(;;))?
  • 您能展示一下您最初是如何填充items 的吗?它是由 Linq 查询创建的吗?如果是问题可能是惰性评估,因此每次迭代 items 时,都会根据查询重新评估值。

标签: c# .net multithreading parallel-processing


【解决方案1】:

我想我已经找到了这里的问题所在。 代码的另一部分是刷新该对象数组,而不是将旧的 Date 属性复制到新对象。

很高兴知道Parallel.ForEach 可以可靠地更新项目并且这是我们的代码的问题。 :) 我只是希望我没有浪费任何人的时间。

感谢您的帮助。 :)

【讨论】:

    【解决方案2】:

    如果你的item是IEnumerable的,可能会出现如下情况:

        private void Test()
        {
    
            var items = GetListItems(); 
            var po = new ParallelOptions();
    
            for (; ; )
                {
                    Parallel.ForEach(items, po, (it) =>
                    {
                        it.Date = DateTime.Now; // Add breakpoint here.
                    });
    
                    var testList = items.ToList();
                }
        }
    
        private IEnumerable<GroupSettings> GetListItems()
        {
            for (int i = 0; i < 3; i++)
                {
                    yield return new GroupSettings(new Random().Next(), Guid.NewGuid() + "@gmail.com") { Date = DateTime.Now };
                }
        }
    

    在这种情况下,没有项目列表,只有枚举器。所以每次你枚举(即,使用 foreach、.ToList 等,调用 GetEnumerator() 简单地说)你都会得到一组新的对象。 所以最好将 IEnumerable 转换为 list(.ToList()),然后继续。

    ps 虽然它可能会导致其他问题

    【讨论】:

      猜你喜欢
      • 2011-04-16
      • 2014-04-11
      • 1970-01-01
      • 1970-01-01
      • 2011-05-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-10-03
      相关资源
      最近更新 更多