C POSIX 线程的互斥体/条件的工作方式因变量所在的位置而异

Posted

技术标签:

【中文标题】C POSIX 线程的互斥体/条件的工作方式因变量所在的位置而异【英文标题】:C POSIX thread's mutex/condition working differently depending on where variable lives 【发布时间】:2012-11-24 00:22:36 【问题描述】:

我在 C POSIX 线程中看到互斥锁/条件的意外行为,具体取决于互斥锁和条件变量是否设置在全局范围(有效)中的结构(有时有效)中。

我在 Mac 上编程,然后在 Linux 机器上运行相同的代码。我从这个例子中复制了代码,它在两台机器上都按预期工作:http://publib.boulder.ibm.com/infocenter/iseries/v7r1m0/index.jsp?topic=%2Fapis%2Fusers_73.htm 此示例在全局范围内具有 pthread_mutex_tpthread_cond_t

pthread_cond_t      cond  = PTHREAD_COND_INITIALIZER;
pthread_mutex_t     mutex = PTHREAD_MUTEX_INITIALIZER;
...
pthread_mutex_lock(&mutex);
...

但是,如果我将其更改为将 cond 和 mutex 存储在结构中,它可以在 Mac 上运行,但 在 Linux 上运行。

以下是我所做更改的概述:

typedef struct _test_data_t 
  pthread_mutex_t cond;
  pthread_cond_t mutex;
 test_data_t;
...
pthread_mutex_lock(&(test_data->mutex));
...

这是我在 Mac 上得到的输出(有效)

创建5个线程
线程阻塞
线程阻塞
线程阻塞
线程阻塞
线程阻塞
唤醒所有等待的线程...
等待线程和清理
主要完成

这是 Linux 上的输出(不起作用)

创建5个线程
线程阻塞 // 永远挂在这里,其他线程无法锁定互斥锁

有谁知道为什么会发生这种情况?我承认我不是 C 专家,所以我不知道从使用全局变量到结构变量的切换会发生什么。

提前感谢您的帮助。


这是代码(为简洁起见,去掉了一些错误检查):

typedef struct _test_data_t 
  int conditionMet;
  pthread_mutex_t cond;
  pthread_cond_t mutex;
 test_data_t;

void *threadfunc(void *parm)

  int           rc;
  test_data_t *test_data = (test_data_t *) parm;

  rc = pthread_mutex_lock((pthread_mutex_t *)&(test_data->mutex));

  while (!test_data->conditionMet) 
    printf("Thread blocked\n");
    rc = pthread_cond_wait(&test_data->cond, &test_data->mutex);
  

  rc = pthread_mutex_unlock(&test_data->mutex);
  return NULL;


void runThreadTest() 

  int NTHREADS = 5;

  int                   rc=0;
  int                   i;

  // Initialize mutex/condition.
  test_data_t test_data;
  test_data.conditionMet = 0;
  rc = pthread_mutex_init(&test_data.mutex, NULL);
  rc = pthread_cond_init(&test_data.cond, NULL);

  // Create threads.
  pthread_t             threadid[NTHREADS];

  printf("Create %d threads\n", NTHREADS);
  for(i=0; i<NTHREADS; ++i) 
    rc = pthread_create(&threadid[i], NULL, threadfunc, &test_data);
  

  sleep(5);
  rc = pthread_mutex_lock(&test_data.mutex);

  /* The condition has occurred. Set the flag and wake up any waiting threads */
  test_data.conditionMet = 1;
  printf("Wake up all waiting threads...\n");
  rc = pthread_cond_broadcast(&test_data.cond);

  rc = pthread_mutex_unlock(&test_data.mutex);

  printf("Wait for threads and cleanup\n");
  for (i=0; i<NTHREADS; ++i) 
    rc = pthread_join(threadid[i], NULL);
  

  pthread_cond_destroy(&test_data.cond);
  pthread_mutex_destroy(&test_data.mutex);

  printf("Main completed\n");

【问题讨论】:

【参考方案1】:

问题在于名为mutex 的成员是pthread_cond_t,而名为cond 的成员是pthread_mutex_t。不必要的演员阵容可能隐藏了这一事实。

typedef struct _test_data_t 
  int conditionMet;
  pthread_mutex_t cond; // <-- poorly named
  pthread_cond_t mutex; // <-- poorly named
 test_data_t;

线程函数中的这一行不需要强制转换:

  rc = pthread_mutex_lock((pthread_mutex_t *)&(test_data->mutex));

但是,您有几个调用没有强制转换(因此编译器应该大声抱怨这些调用)。我开始认为这可能是一个错字,是一个红鲱鱼。

【讨论】:

哇,不错的收获!我很惊讶类型系统没有阻止这一点。 如果它没有被显式覆盖就会有。 天哪,太尴尬了,绝对是这样。我很惊讶它完全可以在我的 Mac 上运行。谢谢! @something_or_nothing:每个人都会在某个时候或某个时候发生,而且这种事情只有其他人才会注意到。我更好奇为什么不使用强制转换的调用没有编译失败? 有警告但我忽略了它们,我想我应该更多地关注它们。我对 C 的缺乏经验让我相信我看到的许多警告都可以安全地忽略,我猜这不是真的

以上是关于C POSIX 线程的互斥体/条件的工作方式因变量所在的位置而异的主要内容,如果未能解决你的问题,请参考以下文章

生产者-消费者问题:介绍POSIX线程的互斥量和条件变量的使用

转载同步和互斥的POSIX支持(互斥锁,条件变量,自旋锁)

线程资源同步——互斥量和条件变量

Linux系统编程-(pthread)线程通信(条件变量)

POSIX条件变量

linux网络编程-posix条件变量(40)