如何为线程不安全的集合编写单元测试

4

我正在使用TDD方法编写一个双向链表。这种集合类型不是线程安全的。为了实现ICollection接口,我的链表类必须具有几个公共属性(包括IsSynchronized和SyncRoot,它们用于提供一种线程安全的集合使用方式)。 这两个属性的代码非常简单:

public bool IsSynchronized { get { return false; } }

private readonly object _syncRoot = new object();
public object SyncRoot { get { return _syncRoot; } }

问题是如何编写正确的单元测试。该测试应该检查正确使用和不正确使用的情况。

2个回答

6
我的列表类必须具有多个公共属性(包括IsSynchronized和SyncRoot)。但实际上不需要这些属性。这个问题可以追溯到.NET版本1,被广泛认为是一个巨大的错误。它创造了一种错误的线程安全感觉,使得很多程序员深陷麻烦。这样的类在某些情况下实际上并不是线程安全的,例如迭代时就不是。在.NET 2.0泛型集合类中完全删除了它。现代集合类应该是泛型的,并且实现ICollection<T>,您不应该再实现ICollection。不幸的是,仍然需要实现IEnumerable,这是我们可能永远无法摆脱的遗留问题。您可以使用显式实现来实现它,这些方法不是公共的。另一个考虑因素是,是否实现ICollection<>是一个好主意。对于链表这样的成员,如Count会变得非常昂贵,它本质上是O(n)。你需要单独跟踪列表中元素的数量,以使其为O(1)。.NET LinkedList<>类也是一个双向链表集合类,确实做到了这一点。

我认为实现 ICollection<T> 并不需要实现 ICollection;泛型版本只继承自 IEnumerable<T> - Dan Tao
谢谢@Dan,你说得很对。LinkedList<>实现了它,让我有些困惑。已更新。 - Hans Passant
抱歉,我是Stack Overflow的新手,还不知道如何在评论中应用标记。同时也很抱歉我的英语不太好。 - Igor Soloydenko
1
在 .net 4 中有线程安全的并发集合,但这些通常具有专门设计用于线程安全原子操作的 API。但在许多情况下,您不希望使集合本身线程安全,而是希望构建在其上的类线程安全。 - CodesInChaos
我认为你对我的初始问题所做的回答都是很好的。多线程类测试的更一般性问题是一个单独的问题,我应该在重新发布到stackoverflow之前先进行“涉及”。(我只是询问了一些常见做法。)所以不要过于谦虚。;) - Igor Soloydenko
显示剩余5条评论

0

我认为这属于不是你的责任范畴。如果使用你的集合类的人想要同步读写,SyncRoot属性会提供一个方便的对象来锁定,仅此而已。(实际上,该属性的目的被广泛误解;正如Hans指出的那样,该属性本身常常被认为是相当无用的。)

如果您确实觉得有足够的理由来实现这个接口,那么同步完全超出了您的类型本身的范围(正如您通过将IsSynchronized属性返回false来表示的那样);因此,在您的端口测试中,它不应该真的需要进行单元测试。

事实上,我认为实现ICollection接口的人经常忽略SyncRoot属性(将其设置为null)。这应该告诉你一些东西。

对于单元测试,您可以简单地验证SyncRoot属性不是null


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接