Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/309.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# T[]是否不比IEnumerable好<;T>;作为参数类型?(考虑线程化挑战)_C#_Multithreading_Collections_Thread Safety_Ienumerable - Fatal编程技术网

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);
    }
}