有可能模拟/伪造一个丢失的锁导致测试失败吗?

12
我正在编写一个细包装Dictionary的代码,旨在实现线程安全。因此需要一些锁定操作,并且大部分逻辑都是确保以线程安全的方式进行锁定和访问。
现在,我想对其进行单元测试。其中一个重要的测试点是锁定行为,以确保其正确性。然而,我从未见过这样的测试方法,所以不确定如何进行。同时,我知道可以使用多个线程来模拟并发情况,但是这种测试方法并不能保证会在出现问题时失败,因为线程调度由操作系统定义。
那么,有哪些方法可以通过单元测试来确保锁定行为的正确性呢?

5
对于这个问题的概念我表示赞同;不过我很好奇为什么您在这种情况下没有使用ConcurrentDictionary - Tim M.
@TimMedora 可移植类库,针对 Windows Phone :( - Earlz
明白。根据我的经验,向被认为是线程安全的类投入数千个操作(跨线程、不同顺序)会很快揭示问题,但当然这并非确定性的。您可以添加一些内部测试钩子来帮助强制执行所需的场景。 - Tim M.
1
@Kimi - 我认为这在某些情况下是一个有效的测试(迭代),因为您可以在迭代下一个项目之前向线程#2发出信号。我不知道在所有情况下(例如两个并发的Add()操作)是否会有相同的确定性范式。 - Tim M.
@TimMedora,这将需要不同的测试,因此我主张精确(确定性地)模拟竞态条件并测试数据完整性,而不是测试锁的调用行为。 - Kimi
显示剩余2条评论
3个回答

3

锁定只是一种实现细节。您应该模拟竞态条件本身,并测试数据是否在测试中失去其完整性。

如果您决定更改字典的实现并使用其他同步原语,这将是一个好处。

测试锁定将证明您在期望的位置调用了锁定语句。而使用模拟竞争条件进行测试可能会揭示您没有预期同步的地方。


这基本上是依赖于字典破坏,如果同时访问它。 - Earlz
不,它应该保持正确的状态,这将由您的测试证明。 - Kimi
2
嗯,这实际上依赖于字典的实现细节。我需要测试一下,确保字典永远不会以并发方式被访问,因为对于我的目的来说,整个东西基本上都不是线程安全的。 - Earlz
字典应该可以并发读取,只有读/写操作需要同步。标准字典将会暴露出并发读/写访问问题,这是您试图避免的,而这些问题将在测试中显现。 - Kimi

2

我基本上做了@Alexei建议的事情。更具体地说,这就是我所做的:

  1. 创建一个PausingDictionary,它基本上只有每个方法的回调,但除此之外只是传递到常规字典
  2. 抽象我的代码(使用DI等),以便在测试时可以使用PausingDictionary而不是常规字典
  3. 在我的单元测试中添加了两个ConcurrentDictionaries。一个称为“Accessed”,另一个称为“GoAhead”。其关键是"action"+Thread.GetHashCode().ToString()的组合(其中action对于每个回调都不同)
  4. 将所有内容初始化为false,并添加一些扩展方法,使其更易于使用
  5. 设置字典的回调以将访问的线程设置为true,但它会在回调中等待GoAhead为true
  6. 从单元测试中启动两个线程。一个线程将访问字典,但由于该线程的GoAhead为false,因此它会坐在那里。然后,第二个线程也将尝试访问字典
  7. 我会断言该线程的Accessed为false,因为我的代码应该将其锁定。

还有更多要做。我还需要模拟一个IList,但我不认为我会这样做。尽管有价值,但这些单元测试肯定不是世界上最容易编写的东西。除了设置代码和虚拟接口实现之外,每个测试最终都成为大约25行非样板代码。锁定很难。证明你的锁定有效更加困难。但令人惊讶的是,这种模式几乎可以测试任何情况。但是,它非常冗长,不适合编写漂亮的测试

所以,尽管编写这些测试很困难,但它完美地工作。当我删除锁定时,它始终失败,而当我重新添加锁定时,它始终通过。

编辑:

我认为这种“控制线程交错”的方法也使得可能测试线程安全性,只要为每个可能的交错编写一个测试即可。对于某些代码,这是不可能的,但我只想说这不仅限于锁定代码。您可以使用相同的方式连续复制线程安全故障,例如foo.Contains(x)然后var tmp=foo[x]


1
我认为仅凭 Dictionary 本身是无法实现可靠的测试的 - 您的目标是使两个调用并行运行,这是不可靠的。您可以尝试使用自定义的 Dictionary(即派生自常规 Dictionary),并为所有 Get/Add 方法添加回调。然后,您将能够根据需要在两个线程中延迟调用... 您需要在两个线程之间进行单独的同步,以使您的测试代码按照您想要的方式运行,并且不会一直出现死锁。

我最终这样做了。我在下面详细阐述了我是如何做到的。 - Earlz

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