【问题标题】:thread sync with pthread conditional variable线程与 pthread 条件变量同步
【发布时间】:2016-07-21 18:21:35
【问题描述】:

我在使用 pthread 条件变量进行线程同步时遇到了一些问题。我有一个线程解析提取一些值的消息,另一个线程使用提取的值增加一些变量。 我使用 pthread 条件变量来同步这两个线程。 第一个线程看起来像下面的 sn-p:

if(parse_ok){
  pthread_mutex_lock(&q_mutex);
  q = extract_value();
  q_changed = true;
  printf("...awake\n");
  pthread_cond_signal(&q_cond_var);
  pthread_mutex_unlock(&q_mutex);
 }

工作线程看起来像下面的sn-p:

while(true){
  pthread_mutex_lock(&q_mutex);                              
  if( !q_changed ){
    std::cout<<"waiting..!"<<std::endl;
    pthread_cond_wait(&q_cond_var, &q_mutex);
  }                                                      
  if(q_changed){
     q_changed = false;
    _actual_q += q;
    _total_q += q;
    _quant_q += q/_fixed_quantity;
  }
  pthread_mutex_unlock(&q_mutex);
}//END of while TRUE                                     

此代码大部分时间都能正常工作。 有时会发生,当我一个接一个地收到很多消息时,睡眠线程会跳过一些唤醒。在工作线程完成工作之前,我是否需要一些信号量来阻止接收线程?如何? 提前致谢。

【问题讨论】:

  • 可能想让 q_changed volatile

标签: c++ c multithreading


【解决方案1】:

这里有几件事可能会咬你:

a) printf 和 cout 不是线程安全的,因此它们的输出可能无法反映代码中等待和唤醒的实际顺序。

b) 在您的代码中无法有效处理虚假唤醒。我会将if( !q_changed ) 条件更改为while (!q_changed),以最大程度地减少您在虚假唤醒时重新等待条件变量所花费的时间(这也将允许您删除最后一个if (q_changed) 条件块)。

编辑(基于 @EOF 等人的 cmets):如果使用现代 C++/C 实现(我假设您不是,因为您使用的是 pthreads),那么很可能 b) 是您的问题。这种情况下的竞争是等待线程可以唤醒然后解锁互斥锁。与此同时,信号线程获取锁和信号。由于您没有等待条件,因此您没有收到信号。但是请注意,这不是问题 - 您仍在处理数据 - 只是在这种情况下您没有向 cout 输出“等待”消息。

【讨论】:

  • C11 标准草案 n1570:7.21.2 Streams 7 每个流都有一个关联的锁,用于防止多个执行线程访问流时的数据竞争,并限制流的交错由多个线程执行的操作。一次只能有一个线程持有这个锁。锁是可重入的:单个线程可以在给定时间多次持有锁。 printf() 线程安全的。
  • @EOF 你忘了C++11和C++14合并了C99,只有C++17才合并C11 C 版。所以基于 C11 的规则是无关紧要的。 (为了完整起见,C++98/03 合并了 C89)。
  • @JesperJuhl 1) 问题标记为 C 和 C++。 2) 在这种情况下,没有个 C 函数在 C++ 中是线程安全的,因为在 C11 之前的 C 中没有 线程。
  • @EOF 1) 我忽略了这一点。 2) 确实。
【解决方案2】:

按照您的代码编写方式,没有保证第二个工作线程将处理新输入每次第一个线程发出信号。但是,您可以扩展 q_changed 变量的使用,以确保发生这种情况。

将您的第一个线程更改为:

if(parse_ok){
    pthread_mutex_lock(&q_mutex);
    while (q_changed) // Always wait in in loop
        pthread_cond_wait(&q_cond_var, &q_mutex);
    q = extract_value();
    q_changed = true;
    pthread_cond_signal(&q_cond_var);
    pthread_mutex_unlock(&q_mutex);
}

并将您的第二个工作线程更改为:

while(true){
    pthread_mutex_lock(&q_mutex);
    while( !q_changed ) // Always wait in a loop
        pthread_cond_wait(&q_cond_var, &q_mutex);
    q_changed = false;
    _actual_q += q;
    _total_q += q;
    _quant_q += q/_fixed_quantity;
    pthread_cond_signal(&q_cond_var);     
    pthread_mutex_unlock(&q_mutex);
}

【讨论】:

    猜你喜欢
    • 2019-04-05
    • 2017-09-27
    • 2018-09-11
    • 2014-05-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多