线程调度/数据竞争条件

时间:2018-10-12 21:55:58

标签: multithreading pthreads scheduling race-condition

在课堂上,我们正在研究线程和竞争条件。据我估计,下面的代码应该有可能输出值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>

3 个答案:

答案 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。