修改IEnumerable<;T>;在Parallel.ForEach中

本文关键字:Parallel ForEach IEnumerable lt 修改 gt | 更新日期: 2023-09-27 18:29:05

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

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

我的案例不涉及任何比赛条件,这个类似的问题与我的案例没有太大关系(因为我对线程安全不感兴趣):

Parallel.ForEach在列表<对象>线程安全

上述代码中的Date是类型DateTime?,并且最初是null。在我的代码中,我从未将其转换为null,我只将其设置为DateTime.Now。问题是,Parallel.ForEach似乎提供items的副本,而不是实际实例,因此无法更新它们。我之所以理解这一点,是因为我得到了先前设置的项目的Datenull值。

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

我问,因为在上面发布的问题链接中,没有人说这(更新平行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放入内部类中(以防副本中的对象引用相同),但行为没有改变。

修改IEnumerable<;T>;在Parallel.ForEach中

我想我已经发现了这里的错误。代码的另一部分是刷新对象数组,而不是将旧的Date属性复制到新对象。

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

谢谢你的帮助。:)

如果您的物品是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尽管它可能会导致其他问题