修改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
的副本,而不是实际实例,因此无法更新它们。我之所以理解这一点,是因为我得到了先前设置的项目的Date
的null
值。
这是预期的行为吗?我找不到任何相关的文档,尽管在将数据提供给线程之前复制数据听起来有点合乎逻辑。
我问,因为在上面发布的问题链接中,没有人说这(更新平行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
放入内部类中(以防副本中的对象引用相同),但行为没有改变。
我想我已经发现了这里的错误。代码的另一部分是刷新对象数组,而不是将旧的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尽管它可能会导致其他问题