Mar*_*ing 9 c# collections ienumerable multithreading thread-safety
一些程序员认为最好将IEnumerable<T>
参数传递给传递的实现,例如List<T>
,这样做的一个常见原因是API可以在更多场景中立即使用,因为更多的集合将与IEnumerable<T>
任何其他特定实现兼容,例如List<T>
.
我越是深入研究多线程开发场景,我就越开始认为这IEnumerable<T>
也不是正确的类型,我将尝试解释下面的原因.
枚举集合时是否收到过以下异常?
收集被修改; 枚举操作可能无法执行.(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);
}
}
Run Code Online (Sandbox Code Playgroud)
我的问题是这个.作为一般规则,对数组而不是枚举进行编码不是更好吗,即使这意味着调用者现在被迫在使用API时将其集合转换为数组?
这不是更清洁,更防弹吗?(参数类型是一个数组而不是)
static void LookAtCollection(int[] collection)
{
foreach (int Value in collection)
{
Thread.Sleep(ITEM_DELAY_MS);
}
}
Run Code Online (Sandbox Code Playgroud)
该数组满足可枚举和固定长度的所有要求,并且调用者知道您将在该时间点对集合的快照进行操作,这可以节省更多错误.
我现在能找到的唯一更好的选择是IReadOnlyCollection,它将更加防弹,因为该集合在项目内容方面也是只读的.
编辑:
@DanielPryden提供了一篇非常好的文章"Arrays被认为有害"的链接.作者的评论"我很少需要一个具有相当矛盾的特性,完全可变,同时又大小固定"和"几乎在所有情况下都有一个比一个更好的工具使用的集合".阵".有点让我相信阵列不像我希望的那样接近银弹,我同意风险和漏洞.我认为现在IReadOnlyCollection<T>
接口是一个比数组和接口更好的选择IEnumerable<T>
,但它现在让我有一个问题:被调用者是否通过IReadOnlyCollection<T>
在方法声明中使用参数类型来强制执行它?或者是否应该允许调用者决定将哪些实现IEnumerable<T>
传递给查看集合的方法?
感谢所有的答案.我从这些回复中学到了很多东西.
这里真正的“问题”是, whileT[]
可能是比理想情况更具体的参数类型,并且允许接收者自由访问写入任何元素(这可能是或可能不是所需的),IEnumerable<T>
但过于笼统。从类型安全的角度来看,调用者可以提供对任何实现 的对象的引用IEnumerable<T>
,但实际上只有某些此类对象才能真正工作,并且调用者没有干净的方法来知道这些对象是哪些。
如果T
是一种引用类型,则 aT[]
本质上对于读取、写入和枚举是线程安全的,但要遵守某些条件(例如,访问不同元素的线程根本不会干扰;如果一个线程在另一个线程读取或读取某项时左右写入一项)枚举它,后一个线程将看到旧的或新的数据。Microsoft 的集合接口都没有提供任何此类保证,也没有提供任何方法让集合可以指示它可以或不能做出什么保证。
我倾向于使用 use T[]
,或者定义 an ,IThreadSafeList<T> where T:class
其中将包含CompareExchangeItem
允许Interlocked.CompareExchange
在项目上使用的成员,但不会包含Insert
无法Remove
以线程安全方式完成的内容。
归档时间: |
|
查看次数: |
505 次 |
最近记录: |