在课堂上,我们正在研究线程和竞争条件。据我估计,下面的代码应该有可能输出值8或9,因为在更新计数器值之前,但是在eax寄存器中将其递增之后,线程1可能会被线程2中断。
int counter = 10;
void *worker(void *arg) {
counter--;
return NULL;
}
int main(int argc, char *argv[]) {
pthread_t p1, p2;
pthread_create(&p1, NULL, worker, NULL);
pthread_create(&p2, NULL, worker, NULL);
pthread_join(p1, NULL);
pthread_join(p2, NULL);
printf("%d\n", counter);
}
但是,当我运行代码时,我总是收到输出8。是编译器对输出进行规范化的一种机制,还是仅代码输出8(不创建竞争条件)? / p>
答案 0 :(得分:1)
在不了解有关您的平台,编译器甚至CPU的许多复杂细节的情况下,我们无法告诉您。该代码在理论上具有竞争条件,但触发起来可能异常困难,甚至不可能。
当然,如果您升级编译器或CPU,更改编译选项,升级操作系统或执行许多其他操作,则它的行为可能会有所不同。
这是比赛条件如此阴险的原因之一。在某些情况下,它们可能无法触发,而在其他位置进行某些更改时,它们总是突然开始发生。
答案 1 :(得分:0)
该代码肯定具有竞争条件。
对于您看到一致的结果,我并不感到惊讶-启动线程需要花费一些时间,因此,就您而言,很有可能第一个线程在第二个线程开始之前就完成了。
尽管如此,该代码显然具有未定义的行为,因为毫无疑问,它具有竞争条件。
答案 2 :(得分:0)
肯定有比赛条件。您之所以没有看到它,是因为与启动线程相比,增量发生的速度如此之快,以至于第一个线程可能在第二个线程甚至开始之前就完成了。如果您使工作量足够大,以至第二个线程启动时第一个线程仍在运行,您将看到竞争状态。
示例:将worker函数修改为循环递减
int counter = 1000000000;
void* worker(void *arg)
{
for (int i = 0; i < 500000000; ++i)
--counter;
return NULL;
}
由于计数器从10亿开始,并且您正在运行2个线程,每个线程将计数器递减5亿,因此,如果不存在竞争条件,则您希望计数器在完成后为0。