竞争条件的定义:竞争条件或竞赛危险是系统或过程中的缺陷,其中过程的输出或结果出乎意料地且严重地依赖于其他事件的顺序或时间。
考虑以下伪代码:
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获得T2秒,则i的值将为14.另一方面,如果T2首先获得锁定l而T1获取第二个,则i的值将为13。
那么,这是否是竞争条件?
更新:经过多次评论和回答后,意见仍然存在分歧。我的观点是“是的,这是竞争条件”类别。实际上我把这个例子作为竞争条件情况,在另一个问题上。 与此同时,我还在“不,这不是竞争条件”类别中阅读了一些有趣的评论。 我想我会解决并得出结论认为这是或不是竞争条件,取决于观察问题的视角/水平。 但是,我还在等待有趣的答案/评论。
答案 0 :(得分: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()
将永远不会完成。我认为大多数人会因为不正确使用条件变量而将其称为竞争条件。
答案 1 :(得分:2)
不,不是。因为它在读取和写入i之前锁定。因此,示例中的读写始终是一致的。当然,你应该在每次操作后解锁,但我想你只是忘了在你的伪代码中添加它。
答案 2 :(得分:2)
不,这是预期的执行顺序之一。竞争不会通过某种锁来保护计数器,从而允许加载 - 修改 - 存储周期同时运行。
但是如果帐户在被操纵时被锁定,输出是否取决于时间?是的,绝对 - 总和不会改变,但两者之间的分歧可能不同。
重要的是受保护价值的一致性 - 无论这两个人从后面拿钱多少次,他们没有得到更多的钱。
答案 3 :(得分:1)
注意:我从Java角度给出答案,因为问题来自previous discussion about the Java Memory Model。
对于“种族条件”的定义似乎存在很多混淆,这就是为什么你会得到不同的答案。
如果您的意思是“数据竞争”,那么Java的上下文中只有一个有效的定义,它由Java Language Specification 17.4.5给出:
当程序包含两个冲突访问(第17.4.1节)时,这些访问不是由先发生关系排序的,它被称为包含数据竞争。
冲突访问在17.4.1中定义:
如果至少有一次访问是写入,则对同一变量的两次访问(读取或写入)被认为是冲突的。
在您的情况下,您的代码包含由17.4.5:
定义的先发生关系监视器上的解锁发生在该监视器上的每次后续锁定之前。
因此,Java代码中的代码中没有数据竞争 - 否则任何人都会使用非Java定义。
其他人评论了“一般种族”,因为这两个代码中的任何一个都可以先运行,但这是一个算法问题:要么你的代码是可并行化的,要么不重要,或者它不是,你应该运行它顺序。但这是一个错误,而不是数据竞争。
答案 4 :(得分:-1)
是肯定的。根据定义它是。你也会遇到可变波动的问题。在这种情况下,无法保证哪个线程将变量从内存加载到哪个寄存器,然后将其保存到内存中。所以可能在某些情况下,一个线程将获得陈旧的值。在许多语言中,您必须以某种方式确保始终获取干净的副本。 (在java volatile中)
http://www.freebsd.org/doc/en/books/developers-handbook/secure-race-conditions.html
我认为这也是一个很好的定义。
阅读:http://dl.acm.org/citation.cfm?id=130623
“隐含地考虑了两个不同的概念:一个与计划确定性(我们称之为一般竞赛)有关,另一个与含有关键部分(我们称之为数据竞赛)的非确定性程序有关。”
所以如果你假设程序总能产生相同的结果,那么我会说这是一场“一般种族”。如果没有,你只是有一个非常奇怪的设计。