我的一个WPF元素数据绑定到一个在Count(Func)
上调用IEnumerable
的属性。该属性显示类似于系统中活动实体的计数(因此需要Func
参数)。
public int ActiveEntitiesCount
{
get
{
return Entities.Count((item) =>
{
//my own code, just a couple of value comparisons, very quick
});
}
}
。
但是IEnumerable
是一个列表,可以随时由另一个线程更改。有时,应用程序崩溃,显然是因为在IEnumerable
函数枚举正在进行时修改了Count
。
我可以放一个try-catch
块,但是如何获取该属性返回的值?
注意:使用锁定可能不切实际,因为我不知道访问IEnumerable
的所有代码
答案 0 :(得分:3)
你需要一些控制并发的方法,或使用并发安全集合提供快照,或(如System.Collections.Concurrent
中的那些。反复尝试操作,捕获异常并重试可能会起作用,但它非常可怕。
集合是否可以在更改时通知此类?这样,您可以将计数保持为字段,而不是每次需要获取属性时都将其记录下来,并以线程安全的方式保留 count 。如果集合变大,计算属性中的集合会变得有点气味。 (如果它实现IList<T>
当然没关系,因为无论如何调用都会被优化......但这是另一回事。)
如果你能告诉我们更多关于背景的信息,我们可能会提供更多帮助。
答案 1 :(得分:3)
您可以通过在列表上运行Count
之前创建列表快照来缓解此问题。像:
return Entities.ToList().Count(item => ...
要真正解决问题,您可以使用任何同步技术使其成为线程安全的,或者重新设计它以避免争用。
[编辑] 可能的解决方案:
public class MyCollection<T> : Collection<T>
{
private readonly object syncRoot = new object();
protected override void SetItem(int index, T item)
{
lock (syncRoot)
base.SetItem(index, item);
}
protected override void InsertItem(int index, T item)
{
lock (syncRoot)
base.InsertItem(index, item);
}
protected override void ClearItems()
{
lock (syncRoot)
base.ClearItems();
}
protected override void RemoveItem(int index)
{
lock (syncRoot)
base.RemoveItem(index);
}
public new int Count(Func<T, bool> predicate)
{
lock (syncRoot)
return Enumerable.Count(this, predicate);
}
}
答案 2 :(得分:0)
您正在以不受支持的方式使用IEnumerable。枚举的来源不应该在枚举“打开”或被使用的生命周期中发生变化。周期。
一种解决方案是要求IEnumeration的所有使用者及其来源的数据源在出于任何原因访问数据之前采用互斥锁。笨手笨脚。
另一种解决方案是维护自己的Count属性,每当数据源发生变化时,都会更新(以线程安全的方式)。这样,您就不需要IEnumerable.Count()。