【问题标题】:Why does this code fail without volatile declaration? [duplicate]为什么没有 volatile 声明此代码会失败? [复制]
【发布时间】:2019-01-19 17:04:36
【问题描述】:

我有以下代码,来自一本书,在 Ubuntu 18.4 上编译,使用 Eclipse -O0 -g3

#include <pthread.h>
#include <stdio.h>

void *myThread( void *arg )
{
  printf( "Thread %d started\n", (int)arg );

  pthread_exit( arg );
}

#define MAX_THREADS 5

int main()
{
  volatile int  i;
  int status;
  int ret;
  pthread_t threadIds[MAX_THREADS];

  for (i = 0 ; i < MAX_THREADS ; i++) {
    ret = pthread_create( &threadIds[i], NULL, myThread, (void *)i );
    if (ret != 0) {
      printf( "Error creating thread %d\n", (int)threadIds[i] );
    }
  }

  for (i = 0 ; i < MAX_THREADS ; i++) {
    printf("-----------  i = %d\n",i);
    ret = pthread_join( threadIds[i], (void **)&status );
    if (ret != 0) {
      printf( "Error joining thread %d\n", (int)threadIds[i] );
    } else {
      printf( "Status = %d\n", status );
    }
  }

  return 0;
}

这段代码正常工作并产生了输出

-----------  i = 0
Thread 3 started
Thread 0 started
Status = 0
-----------  i = 1
Thread 2 started
Thread 1 started
Status = 1
-----------  i = 2
Status = 2
-----------  i = 3
Status = 3
-----------  i = 4
Thread 4 started
Status = 4

但是,如果变量 i 声明处的 volatile 关键字被忽略,则会产生以下输出:

Thread 1 started
Thread 0 started
Thread 2 started
Thread 3 started
-----------  i = 0
Status = 0
-----------  i = 1
Status = 1
-----------  i = 1
Error joining thread -947054848
-----------  i = 2
Status = 2
-----------  i = 1
Error joining thread -947054848
-----------  i = 2
Error joining thread -955447552
-----------  i = 3
Status = 3
-----------  i = 1
Error joining thread -947054848
-----------  i = 2
Error joining thread -955447552
-----------  i = 3
Error joining thread -963840256
-----------  i = 4
Thread 4 started
Status = 4
-----------  i = 1
Error joining thread -947054848
-----------  i = 2
Error joining thread -955447552
-----------  i = 3
Error joining thread -963840256
-----------  i = 4
Error joining thread -1073744128

请帮助我确定导致此问题的可能原因。

【问题讨论】:

  • @Alerra 你错了。 volatile 不保证一致性,锁定访问的原子性。只说该变量容易产生副作用,即强制在使用前读取并在更改后存储,仅此而已
  • (void *)i,不应该是(void*)&amp;i吗?
  • 在从pthread_join() 返回错误时,打印值或ret(错误号)比打印线程ID 提供更多信息。然而,这并不能解释出了什么问题。
  • @DanFarrell:将i 的地址传递给pthread_create() 会导致各种同步问题——不同的线程不一定会看到i 中当时的值当pthread_create() 被调用时。
  • 如果代码确实是一本书的逐字记录,也许你应该告诉我们它的名字——然后然后烧掉它。

标签: c linux multithreading


【解决方案1】:

在您的代码中:

int status;

// ...

pthread_join( threadIds[i], (void **)&status );

这是一个严格的别名违规。 pthread_joinvoid * 类型的值写入该位置,但是您提供了 int 的位置。这会导致未定义的行为,这意味着任何事情都可能发生。

您的系统上发生的可能是写入越界(int = 4 字节,void * = 8 字节),导致堆栈混乱。

您可以通过以下方式解决此问题:

void *status;
// ...
pthread_join( threadIds[i], &status );

以后,如果您想使用%d 打印状态,请使用(int)status 作为参数。

【讨论】:

  • 不仅是严格的别名违规,而且可能是 C17 6.3.2.3 §5 和 §6 下的 UB,以防 int 不能表示指针,反之亦然。现在很可能是使用 64 位计算机。
  • @Lundin 通常是的,尽管 OP 在 Linux 上指定了 gcc,但我希望它会定义该操作(这是实现定义的);我们希望往返 int -> void * -> int 有效。逆行可能会明显失败
猜你喜欢
  • 2015-05-19
  • 2017-09-18
  • 1970-01-01
  • 1970-01-01
  • 2016-03-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多