用gdb / lldb模拟竞争条件是否可行?

时间:2013-03-22 07:13:36

标签: multithreading algorithm race-condition

我想知道使用调试器自动测试竞争条件是否可行。

例如,您想要测试多线程队列的映像。除此之外,您还需要测试一下,您可以同时拨打enqueue()dequeue()

一个简单的单元测试可以启动两个线程,每个线程分别在一个循环中调用enqueue()dequeue()并检查结果:

// thread A
for( int i=0; i<count; i+=1 ) {
  enqueue( queue, i );
}

// thread B
for( int i=0; i<count; i+=1 ) {
  ASSERT( i == dequeue( queue ) );
}

现在,在gdblldb中运行单元测试的聪明的测试驱动程序应该能够等待在两个循环中设置的断点,然后使用调试器si (步骤指令)命令模拟两个线程的所有可能的交错。

我的问题不在于技术上是否可行(确实如此)。我想知道的是:

假设enqueue()函数有10个指令且dequeue()函数有20个 - 测试必须尝试多少个不同的交错?

1 个答案:

答案 0 :(得分:2)

让我们看看......

如果我们每个只有2条指令:a,b和A,B:

A,B,A,B
一,A,B,B
一,A,B,B
A,A,B,B
A,A,B,B
A,B,A,B

那是6。

对于a,b,c和A,B,C:

A,B,C,A,B,C
A,B,A,C,B,C
A,B,A,B,C,C
A,B,A,B,C,C
一,A,B,C,B,C
一,A,B,B,C,C
一,A,B,B,C,C
一,A,B,B,C,C
一,A,B,B,C,C
一,A,B,C,B,C
A,A,B,C,B,C
A,A,B,B,C,C
A,A,B,B,C,C
A,B,A,B,C,C
A,A,B,B,C,C
A,A,B,B,C,C
A,B,A,B,C,C
A,A,B,C,B,C
A,B,A,C,B,C
A,B,C,a,b,c

那是20,除非我遗漏了什么。

如果我们将它推广到每个N指令(比如N是26)并以... zA ... Z开始,那么z将有27个可能的位置(从A之前到Z之后), y最多27个位置,x最多28个,w最多29个等。这表明最差的因子。然而,实际上,它还不到那个,但我有点懒,所以我将使用一个简单程序的输出来计算可能的“交错”的数量,而不是得出确切的公式:

1 & 1 -> 2
2 & 2 -> 6
3 & 3 -> 20
4 & 4 -> 70
5 & 5 -> 252
6 & 6 -> 924
7 & 7 -> 3432
8 & 8 -> 12870
9 & 9 -> 48620
10 & 10 -> 184756
11 & 11 -> 705432
12 & 12 -> 2704156
13 & 13 -> 10400600
14 & 14 -> 40116600
15 & 15 -> 155117520
16 & 16 -> 601080390

因此,通过这些结果,您可以得出结论,虽然这个想法是正确的,但是使用它进行代码验证需要花费不合理的时间。

此外,您应该记住,您不仅需要考虑指令执行的顺序,还需要考虑队列的状态。这将增加迭代次数。

编辑:这是程序(在C中):

#include <stdio.h>

unsigned long long interleavings(unsigned remaining1, unsigned remaining2)
{
  switch (!!remaining1 * 2 + !!remaining2)
  {
  default: // remaining1 == 0 && remaining2 == 0
    return 0;

  case 1: // remaining1 == 0 && remaining2 != 0
  case 2: // remaining1 != 0 && remaining2 == 0
    return 1;

  case 3: // remaining1 != 0 && remaining2 != 0
    return interleavings(remaining1 - 1, remaining2) +
           interleavings(remaining1, remaining2 - 1);
  }
}

int main(void)
{
  unsigned i;
  for (i = 0; i <= 16; i++)
    printf("%3u items can interleave with %3u items %llu times\n",
           i, i, interleavings(i, i));
  return 0;
}

<强> EDIT2

顺便说一句,如果您模拟伪代码,由于与调试器接口以及由于各种上下文切换,您还可以节省一个数量级(或两个)的开销。有关示例实现,请参阅this answer to a somewhat related question。与直接执行相比,这也可以让您对线程之间的切换进行更精细的控制。