9

竞态条件的定义:竞态条件或竞态危害是系统或过程中的缺陷,其中过程的输出或结果出乎意料地严重依赖于其他事件的顺序或时间。

考虑以下伪代码:

    Global variable i initialized to 6;
    Thread 1: 
        acquire(lock l)
        increment global variable i, i.e. i++;

    Thread 2: 
       acquire(lock l)
       double the value of global var i, i.e.: i*=2;

如果 T1 先获得锁 l,然后 T2,则 i 的值将是 14。另一方面,如果 T2 先获得锁 l,然后 T1,则 i 的值将是 13。

那么,这是否是竞争条件?

更新:经过多次评论和回答,意见仍然存在分歧。我的观点是“是的,这是一个竞争条件”类别。实际上,我在另一个问题上给出了这个例子作为竞争条件的情况。同时,我还在“不,这不是竞争条件”类别中看到了一些有趣的评论。我想我会解决并得出结论,这取决于一个人看待问题的角度/水平,这是否是一种竞争条件。但是,我仍在等待有趣的答案/评论。

4

5 回答 5

8

我认为示例算法是否具有竞争条件取决于算法的预期用途。

修改没有数据竞争i- 这些访问是序列化的,并且相对于彼此原子地发生。

但是,如果增量发生在乘法之前(反之亦然)对算法的正确性很重要,那么就会存在竞争,并且必须使用其他方式来同步算法的执行。如果算法应该是一种复杂的计算方式i * 2 + 1(就像使用线程执行计算一样荒谬),那么就会出现竞争条件。

考虑以下程序片段:

int data;

pthread_cond_t condvar = PTHREAD_COND_INITIALIZER;
pthread_mutex_t mux = PTHREAD_MUTEX_INITIALIZER;

void* wait_for_data(void*)
{
    pthread_mutex_lock( &mux);
    pthread_cond_wait( &condvar, &mux);

    puts("got the data: %d\n", data);

    pthread_mutex_unlock( &mux);

    return 0;
}


void* set_data(void*)
{
    pthread_mutex_lock( &mux);

    data = 42;

    pthread_cond_signal( &condvar);

    pthread_mutex_unlock( &mux);

    return 0;
}

两个线程本质上是完全互斥的——没有数据竞争。但是,如果在等待set_data()条件变量之前发出信号,则永远不会完成。我认为由于条件变量的使用不当,大多数人会称其为竞争条件。wait_for_data()wait_for_data()

于 2012-08-17T15:38:26.703 回答
2

不,这不对。因为它在读取和写入 i 之前锁定。因此,您示例中的读取和写入始终是一致的。当然,您应该在每次操作后解锁,但我猜您只是忘记在伪代码中添加它。

于 2012-08-17T13:59:50.350 回答
2

不,这是预期的执行顺序之一。竞争不会用某种锁来保护计数器,从而允许加载-修改-存储周期同时运行。

编辑0:

@Gheorghe,想想一个联合银行账户的例子,两个人同时在不同的银行办事处取钱。每个地点的职员都需要检查账户余额、发放现金并记下新余额。如果这对于余额来说不是“原子的”,即在此操作期间帐户没有“锁定”,那么他们最终可能会在他们两人之间获得比他们在银行中的更多的钱。银行不喜欢这样。

但是如果账户在被操纵时被锁定,输出是否取决于时间?是的,绝对 - 总和不会改变,但他们两个之间的分配可能不同。

重要的是受保护价值的一致性——无论这两个家伙以什么顺序和多少次从背后拿钱,他们得到的钱都不会比他们原来的多。

于 2012-08-17T14:00:24.530 回答
1

注意:我从 Java 的角度给出一个答案,因为这个问题源于之前关于 Java 内存模型的讨论。

围绕“竞争条件”的定义似乎有很多混淆,这就是为什么你会得到不同的答案。

如果您的意思是“数据竞争”,那么在 Java 的上下文中只有一个有效的定义,它由Java 语言规范 17.4.5给出:

当一个程序包含两个冲突的访问(第 17.4.1 节)时,这些访问没有按发生前的关系排序,则称为包含数据竞争。

冲突访问在17.4.1中定义:

如果至少有一次访问是写入,则对同一变量的两次访问(读取或写入)称为冲突。

在您的情况下,您的代码包含由17.4.5定义的发生前关系:

监视器上的解锁发生在该监视器上的每个后续锁定之前。

因此,在 Java 上下文中,您的代码中没有数据竞争 - 任何人说否则都是在使用非 Java 定义。

其他人评论了“一般竞赛”,因为这两个代码中的任何一个都可以先运行,但这是一个算法问题:要么你的代码是可并行的,这无关紧要,要么不是,你应该按顺序运行它。但这是一个错误,而不是数据竞赛。

于 2012-08-18T08:18:50.707 回答
-1

是的。根据定义,它是。此外,您还会遇到可变波动性的问题。在这种情况下,无法保证哪个线程将变量从内存加载到哪个寄存器,然后将其保存到内存中。所以在某些情况下,一个线程可能会得到一个陈旧的值。在许多语言中,您必须以某种方式确保始终获取干净的副本。(在 Java 易失性中)

http://www.freebsd.org/doc/en/books/developers-handbook/secure-race-conditions.html

我认为这也是一个很好的定义。

阅读:http ://dl.acm.org/citation.cfm?id=130623

“已经隐含地考虑了两个不同的概念:一个与旨在确定性的程序(我们称之为一般竞争)有关,另一个与包含关键部分的非确定性程序(我们称之为数据竞争)有关。”

因此,如果您假设该程序始终产生相同的结果,那么我会说这是一场“普遍的比赛”。如果没有,你只是有非常奇怪的设计。

于 2012-08-17T13:58:34.737 回答