C# T[]是否不比IEnumerable好<;T>;作为参数类型?(考虑线程化挑战)
一些程序员认为传递C# T[]是否不比IEnumerable好<;T>;作为参数类型?(考虑线程化挑战),c#,multithreading,collections,thread-safety,ienumerable,C#,Multithreading,Collections,Thread Safety,Ienumerable,一些程序员认为传递IEnumerable参数比传递List等实现更好,这样做的一个普遍原因是,API可以立即在更多的场景中使用,因为与任何其他特定实现(例如List)相比,更多的集合将与IEnumerable兼容 我越深入研究多线程开发场景,我就越开始认为IEnumerable也不是正确的类型,下面我将尝试解释原因 您在枚举集合时是否收到过以下异常 收集被修改;枚举操作可能无法执行。(InvalidOperationException) 基本上,造成这种情况的原因是,您在一个线程上获得集合,而
IEnumerable
参数比传递List
等实现更好,这样做的一个普遍原因是,API可以立即在更多的场景中使用,因为与任何其他特定实现(例如List
)相比,更多的集合将与IEnumerable
兼容
我越深入研究多线程开发场景,我就越开始认为IEnumerable
也不是正确的类型,下面我将尝试解释原因
您在枚举集合时是否收到过以下异常
收集被修改;枚举操作可能无法执行。(InvalidOperationException)
基本上,造成这种情况的原因是,您在一个线程上获得集合,而其他人在另一个线程上修改集合
为了避免这个问题,我养成了在枚举集合之前拍摄集合快照的习惯,方法是将集合转换为方法内的数组,如下所示:
static void LookAtCollection(IEnumerable<int> collection)
{
foreach (int Value in collection.ToArray() /* take a snapshot by converting to an array */)
{
Thread.Sleep(ITEM_DELAY_MS);
}
}
数组满足可枚举和固定长度的所有要求,调用方知道您将在该时间点对集合的快照进行操作,这可以节省更多的bug
我现在能找到的唯一更好的选择是IReadOnlyCollection,它将更加防弹,因为该集合在项目内容方面也是只读的
编辑:
@DanielPryden提供了一个链接,指向一篇非常好的文章。作者的评论“我很少需要一个集合,它具有完全可变、同时大小固定的矛盾特性”和“在几乎所有情况下,都有比数组更好的工具可供使用”。这让我相信数组并不像我所希望的那样接近银弹,我同意这些风险和漏洞。我认为现在,IReadOnlyCollection
接口是比数组和IEnumerable
更好的选择,但现在它给我留下了一个问题:被调用方是否通过在方法声明中使用IReadOnlyCollection
参数类型来强制执行它?还是应该允许调用者决定将什么IEnumerable
实现传递到查看集合的方法中
谢谢你的回答。我从这些回答中学到了很多。这个问题并不是线程特有的。尝试在显式或隐式调用
GetEnumerator
的循环中修改集合。结果是一样的。这是一个非法操作。决不允许线程访问与其他线程相同的对象。
您应该始终复制变量/对象/数组,并在线程中解析它们
完成后,它应该回调主线程,以便线程可以更新当前值/合并值
如果两个物体可以同时到达同一个物体,你会得到一场拔河,或者异常或奇怪的行为
想象一下这样的线程:
你有老板和两名员工。老板把手中的信复印一份交给员工。
每个员工都有自己的任务,一个收集清单上的所有内容,另一个检查清单上的项目是否需要从供应商处订购。
他们每个人都在自己的私人房间里,老板只是把文件放在桌子上,他有时会检查一下,告诉别人此刻有多少
一号员工带着订购的物品返回,并将其交给主管,主管将其转交给物流部门。
一小时后,两名员工返回并更新了库存数量列表。主任扔掉旧名单,换上新名单
现在是一个列表的场景
老板也给了同样的指示。员工一拿到名单,开始给第一个供应商打电话。员工2获取列表并开始挑选物品。主管想看一看名单,但名单不见了,整个销售渠道都陷入了瘫痪,因为没有准确的信息,因为名单被占用了。
第一名员工已完成呼叫,希望更新其列表。他不能,因为列表已满,他无法继续下一项,因为列表已不存在,所以他一直在玩弄大拇指或大发雷霆。
当员工2准备好他的列表时,员工获取列表并尝试完成调用订单状态,但主管闯入并获取列表并将信息传递给销售团队。
董事和员工都开始为这份名单争吵,结果导致没有工作做,公司倒闭
这就是为什么您总是需要在线程中处理独立副本的原因,除非您知道只有该线程专门需要访问该资源,因为您永远不知道该特定线程何时将使用该资源执行某些操作,因为您不控制CPU定时
我希望这能有所帮助。IEnumerable背后的一个想法是它是一个
LINQ
查询的结果。由于查询不是声明而不是执行的代码,因此只有在迭代foreach
或将项复制到数组或其他结构时才会调用IEnumerable
的结果。现在,如果我们想将结果存储在一个静态结构中,比如数组
,我们必须在迭代之前更新它以及IEnumerable
,否则我们将收到过时的数据IEnumerable.ToArray
的意思是:更新集合并生成其静态副本。所以我的答案是,如果我们必须在广告中进行更新
static void LookAtCollection(int[] collection)
{
foreach (int Value in collection)
{
Thread.Sleep(ITEM_DELAY_MS);
}
}