0

这是我的线程学习测试的一段代码:

int mylock = 0;

void *r1(void *x)
{
  puts("entered r1");
  int *p;
  p = (int *)x;
  int i = *p;
  sleep(1);
  *p = --i;
  printf("r1: %d\n",*p);
  mylock = 1;
  printf("r1: done\n");
#ifdef USETHREADS
  pthread_exit(0);
#endif
}

void *r2(void *x)
{
  puts("entered r2");
  if (!mylock) {
          puts("r2 is waiting...");
          while (!mylock)
                printf("");
  }
  int *p;
  p = (int *)x;
  int i = *p;
  *p = ++i;
  sleep(1);
  printf("r2: %d\n",*p);
  printf("r2: done\n");
#ifdef USETHREADS
  pthread_exit(0);
#endif
}

main()
{
  int i1,i2;
  i1 = 1;
  i2 = 2;
  printf("i1: %d\n", i1);
#ifdef USETHREADS
  pthread_t r1_thread, r2_thread;
  pthread_create(&r1_thread, NULL, r1, &i1);
  pthread_create(&r2_thread, NULL, r2, &i1);
  pthread_join(r1_thread, NULL);
  pthread_join(r2_thread, NULL);
#else
  r1(&i1);
  r2(&i1);
#endif
  printf("i1: %d\n", i1);
  return 0;
}

所以有两个线程,一个增加 i1,一个减少(我知道 pthread 中的“互斥锁”,虽然此时不使用),所以为了避免竞争条件,我创建了 mylock(我猜它伪造互斥锁),这让我感到惊讶进程是否在等待 mylock 更改值时卡在未定义的循环中,除非printf在等待循环中调用,使用 printf 调用它会在预期的 2 秒内退出,这是 linux 的谜吗?

4

2 回答 2

4

您的程序通过mylock不同步访问来调用未定义的行为。在访问它之前以及完成后,您需要调用pthread_mutex_lock(在您选择保护状态的互斥锁上) 。当然,then可能是没用的;您可以直接用于锁定。mylockpthread_mutex_unlockmylockpthread_mutex_lock

话虽如此,如果您尝试学习线程,那么您将以完全错误的方式进行操作。您不会从尝试推出自己的同步原语开始。在大多数情况下,即使您是专家,也不应该这样做,除非您有非常不寻常的需求,即使那样,这也可能是个坏主意。给自己一个好的线程教程,教你如何正确使用同步原语,然后使用它们

于 2013-09-15T19:19:40.940 回答
0

即使使用printf也可能会卡住,因为允许编译器假设它printf不会改变mylock(这实际上是真的,不会printf改变)。mylock

在这种情况下,给定以下形式的循环:

while (!x)
    some_code;

编译器在循环内部“环顾四周”some_code以确定它对x. 例如,如果知道循环每次倒数一个,例如 with或类似的,编译器可以在循环顶部x -= 1采样一次,然后“展开”循环:如果 x >= 4,它可以x将其替换为:

while (x >= 4) {
    some_code;
    some_code;
    some_code;
    some_code;
}
switch (x) {
case 3:
    some_code;  /* and, no "break" */
case 2:
    some_code;
case 1:
    some_code;
}

通过消除各种测试来加快运行时间。(如果x是“死”,即在循环之后不再使用,在三种清理情况下,编译器也可以删除该x -= 1部分。)

这里,x(或者更确切地说,my_lock)根本没有改变,所以循环是无限的:编译器可以并且确实将 替换为while

if (my_lock)
    for (;;) continue;

在这种特殊情况下,您可以通过my_lock使用volatile限定符声明来使事情正常进行:

volatile int my_lock;

这告诉编译器变量发生了一些不寻常的事情:它以编译器无法通过正常方式感知的方式进行了修改。但这并不能保证,而 pthread 互斥锁确实有保证。该volatile关键字更多地意味着编写 pthread 互斥锁实现的任何人都可以使用的工具,而不是最终用户。

于 2013-09-15T18:47:28.047 回答