【问题标题】:pthread waiting condition does not always wake on signalpthread 等待条件并不总是在信号上唤醒
【发布时间】:2013-05-27 04:37:51
【问题描述】:

我目前正在使用条件变量来同步两个线程 (pthread),并且遇到了一个意外的行为,即使我已经验证了一个线程已经在等待某个条件,但当另一个线程发出该条件的信号时它不会唤醒.

值得注意的是,我已经在桌面环境中运行了它,并且它按预期运行,但是当我使用 uclibc 在嵌入式环境中运行程序时会出现这个问题。

为了进行故障排除,我将我的代码剥离为仅执行锁定/解锁/信号的两个线程,如下所示:

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

pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t mutex2 = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t condition1 = PTHREAD_COND_INITIALIZER;
pthread_cond_t condition2 = PTHREAD_COND_INITIALIZER;
bool predicate1 = false;
bool predicate2 = false;

static void * ThreadFunc2(void * arg) {
    sleep(1);    // For testing purposes, ensures this thread is run after Thread1

    pthread_mutex_lock(&mutex2);
    while(1) {
        pthread_mutex_lock(&mutex1);
        // Do some work - Eg receive some data from a socket
        predicate1 = false;
        pthread_cond_signal(&condition1);
        pthread_mutex_unlock(&mutex1);

        predicate2 = true;
        while(predicate2 == true)
            pthread_cond_wait(&condition2, &mutex2);

        // Do some more work - Eg send response data to socket
    }
}

static void * ThreadFunc1(void * arg) {
    int result;

    pthread_mutex_lock(&mutex1); 
    while(1) {
        predicate1 = true;
        while(predicate1 == true)
            pthread_cond_wait(&condition1, &mutex1);

        // Do some work - Eg process data on the socket and prepare response data to be sent
        pthread_mutex_lock(&mutex2);
        predicate2 = false;
        pthread_cond_signal(&condition2);
        pthread_mutex_unlock(&mutex2);
    }
}

int main(int argc, char * argv[]) {
    pthread_t thread1Id, thread2Id;

    pthread_create(&thread1Id, NULL, ThreadFunc1, NULL);
    pthread_create(&thread2Id, NULL, ThreadFunc2, NULL);

    while(1) {
        sleep(1);
    }

    return 0;
}

如果我排除与 mutex2/condition2/predicate2 相关的所有语句,这两个线程将按预期协同工作。

使用上面列出的代码,在很短的时间后(由于所有工作都已被剥离,每个循环运行得非常快)ThreadFunc1 中的 condition1 等待不会唤醒,即使它由 Threadfunc2 发出信号,导致应用程序正在运行停止。

为了帮助我进行调试,我重新定义了 pthread_* 函数,以便在调用实际 pthread_* 函数之前使用匹配的行号将消息打印到标准输出。这使我能够跟踪每个 pthread 操作的流程,并验证信号是否正在发送到已经等待的状态。

任何人都可以帮助我阐明上述实施中可能存在的任何潜在问题吗?

提前感谢您的任何建议。

【问题讨论】:

  • 您的示例代码从不将predicate2 设置为false
  • 我添加了 @caf 所指出的缺失的 predicate2 语句 - 谢谢。不幸的是,问题仍然存在。
  • 你能发布完整的测试程序(听起来应该不会更多),用于构建程序的命令以及有关使用的平台和工具版本的信息?我无法通过添加足够的脚手架来运行线程和几个puts() 调用来确保正在取得进展。这是在带有 gcc 4.6.1 的 x86 Linux 3.0 上。
  • 我开始怀疑是否是由于使用我的嵌入式板支持包(使用 buildroot 的 BSP)构建的工具链造成的。我的平台是基于 m68k 的,使用 uclibc 0.9.31 和 gcc 4.4.1。我在另一个较旧的平台(基于 avr32,使用 uclibc 0.9.29 和 gcc 4.2.1)上进行了测试,问题没有重现。
  • @wei:你可能是对的。您是否将-pthread 选项传递给gcc? (注意:这是-pthread,与-lpthread 不同)。如果您可以使用最新版本的uclibc 重现它,您可以将其报告为错误。

标签: c linux multithreading pthreads


【解决方案1】:

您的错误是在调用 pthread_cond_wait() 之后,您没有解锁条件变量使用的互斥锁。

例如 pthread_cond_wait() 在线程被阻塞时在内部解锁互斥锁,但它在唤醒时重新获取锁,您需要显式释放它。

有关 cond 的更多详细信息,请参阅本教程。变量:https://computing.llnl.gov/tutorials/pthreads/#ConditionVariables

【讨论】:

  • 感谢您的回答。每次等待后我都没有解锁每个互斥锁,因为在循环重新启动之前对各个互斥锁没有依赖关系。根据我的理解(这可能是不正确的),等待语句的唯一要求是互斥锁必须已经被当前线程锁定,这是每个线程执行 pthread_cond_wait 语句的情况。我是否遗漏了其他要求,例如要求在后续 pthread_cond_wait 语句之前释放和重新获取互斥锁?谢谢。
  • 在显示的代码中,无需解锁这些互斥锁。
  • @wei 是什么让编译器无法将 predicate2 保存在寄存器中?如果您获取并释放互斥锁,您将通过编译器内存屏障。我不确定 pthread_cond_wait() 是不是内存屏障。
  • @caf 是什么告诉编译器在调用 pthread_cond_wait() 期间谓词更改的值?我怀疑互斥操作在这里的用处是它们提供的内存屏障。不过我只是猜测。
  • @gby:POSIX 列出了"functions that synchronize memory with respect to other threads",其中包括pthread_cond_wait() 函数。
【解决方案2】:

我遇到了类似的问题。就我而言,有时信号是在阻塞线程等待之前发送的。这种情况下的行为是两个线程都“卡住”了。 我们通过添加一个通知信号已发送的标志来解决它。

【讨论】:

  • 当等待线程卡在 pthread_cond_wait 时,您是如何设法唤醒它的?您是否使用 timedwait 而不是 wait,并以与我上面使用的谓词变量相同的方式检查标志变量?谢谢。
  • 如果设置了标志,它没有等待,只是继续。这样我们就没有错过信号。
【解决方案3】:

解决方案 - 见下面的解释
将 pthread_mutex_unlock() 放在信号调用 pthread_cond_signal() 之前而不是之后应该可以解决问题

...
pthread_mutex_lock(&mutex1);
predicate1 = false;
pthread_mutex_unlock(&mutex1);
pthread_cond_signal(&condition1);
...

在函数 ThreadFunc2 和线程 1 中类似

...
pthread_mutex_lock(&mutex2);
predicate2 = true;
pthread_mutex_unlock(&mutex2);
pthread_cond_signal(&condition2);
...

在函数 ThreadFunc1 中。

解释在你的程序线程2进入信号调用

pthread_cond_signal(&condition1); //  thread 2 with mutex1 locked

mutex1 被锁定。线程1只能离开阻塞

pthread_cond_wait(&mutex1);       //  thread 1 leaves only after mutex1 unlocked

调用本身锁定 mutex1,这是此函数调用的保证行为 - 这意味着它应该被所有其他线程解锁才能继续。如果您有一个 pthread_cond_signal() 的实现,它会阻塞直到接收信号的线程继续运行,那么当它进入调用并锁定相应的互斥锁时会导致死锁。这也可以解释为什么一个环境可以简单地工作而另一个不能正常工作:例如,当您的桌面环境没有对 pthread_cond_signal() 的阻塞调用而您的嵌入式环境有时。

【讨论】:

  • 我已经对您的解决方案进行了试验,它看起来解决了我在非常相似的环境中遇到的问题。但是,该顺序确实违反直觉,并且 pthread_cond(3) 手册页中给出的示例解锁了 //after// 发送条件。我并不完全相信你的解释。如果是正确的,如何
  • ...评论继续(编辑时延迟5分钟)...所以:您的意思是解锁可以立即控制所有等待锁定的线程吗?否则,不能保证在解锁后发送发送会改变任何内容。我错了吗?
猜你喜欢
  • 2015-07-05
  • 1970-01-01
  • 2012-03-18
  • 2010-10-29
  • 2019-09-27
  • 2016-06-21
  • 2018-02-07
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多