【问题标题】:unexpected behavior when using conditional variables (c, gcc)使用条件变量(c、gcc)时的意外行为
【发布时间】:2017-11-03 20:33:57
【问题描述】:

我正在尝试学习如何在 C 中正确使用条件变量。 作为我自己的练习,我正在尝试制作一个带有 2 个线程的小程序,这些线程在无限循环中打印“Ping”,然后是“Pong”。

我写了一个小程序:

pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

void* T1(){
    printf("thread 1 started\n");

    while(1)
    {
        pthread_mutex_lock(&lock);
        sleep(0.5);
        printf("ping\n");
        pthread_cond_signal(&cond);
        pthread_mutex_unlock(&lock);
        pthread_cond_wait(&cond,&lock);
    }
}

void* T2(){
    printf("thread 2 started\n");

    while(1)
    {
        pthread_cond_wait(&cond,&lock);
        pthread_mutex_lock(&lock);
        sleep(0.5);
        printf("pong\n");
        pthread_cond_signal(&cond);
        pthread_mutex_unlock(&lock);
    }
}

int main(void)
{
    int i = 1;
    pthread_t t1;
    pthread_t t2;



    printf("main\n");
    pthread_create(&t1,NULL,&T1,NULL);
    pthread_create(&t2,NULL,&T2,NULL);
    while(1){
        sleep(1);
        i++;
    }
    return EXIT_SUCCESS;
}

当运行这个程序时,我得到的输出是:

main
thread 1 started
thread 2 started
ping

知道程序没有按预期执行的原因是什么吗?

提前致谢。

【问题讨论】:

  • sleep 是一个非标准函数,但我认为sleep(0.5) 实际上是sleep(0),因为它通常需要一个整数类型作为参数。
  • @Bathsheba sleep 与 pthread 一样标准。假设您在具有 pthread 的系统上拥有它是非常安全的(并且它需要一个无符号作为参数)。

标签: c multithreading concurrency


【解决方案1】:

sleep 采用整数,而不是浮点数。不确定sleep(0) 在您的系统上做了什么,但这可能是您的问题之一。

你需要在调用pthread_cond_wait时持有互斥锁。

裸条件变量(即不指示在其他地方读取条件的条件变量)几乎总是错误的。条件变量表明我们正在等待的东西可能已经准备好被消费了,它们不是用于信号的(不是因为它是非法的,而是因为很难让它们正确地用于纯信号)。所以一般情况下会是这样的:

 /* consumer here */
 pthread_mutex_lock(&something_mutex);
 while (something == 0) {
     pthread_cond_wait(&something_cond, &something_mutex);
 }
 consume(something);
 pthread_mutex_unlock(&something_mutex);

 /* ... */

 /* producer here. */
 pthread_mutex_lock(&something_mutex);
 something = 4711;
 pthread_cond_signal(&something_cond, &something_mutex);
 pthread_mutex_unlock(&something_mutex);

拿着锁睡觉是个坏主意。

T1T2 不是用作 pthread_create 的函数的有效函数,它们应该接受参数。做对了。

您在 cond_signal 和 cond_wait 之间的每个线程中都在与自己竞争,因此每个线程可能一直都在向自己发出信号并不是不可信的。 (在对 pthread_cond_wait 的调用中正确持有互斥锁可能会有所帮助,也可能不会,这就是为什么我说正确获取裸条件变量很难,因为确实如此)。

【讨论】:

    【解决方案2】:

    首先,您永远不应该使用 sleep() 来同步线程(如果您需要降低输出速度,请使用 nanosleep())。您可能需要(通常使用)共享变量ready 让每个线程知道他可以打印消息。在创建pthread_cond_wait() 之前,您必须获得锁,因为pthread_cond_wait() 函数将阻塞条件变量。它应该被调用线程锁定的互斥锁或未定义的行为结果调用。

    步骤是:

    1. 获取锁
    2. 在guard[*]中使用共享变量等待一段时间
    3. 做事
    4. 更改共享变量的值以进行同步(如果您有的话)和您完成工作的信号/广播
    5. 解除锁定

    第 4 步和第 5 步可以颠倒。

    [*]您使用pthread_cond_wait() 来释放互斥锁并阻塞条件变量上的线程,当使用条件变量时,总是有一个布尔谓词涉及与每个条件相关联的共享变量等待,如果线程应该继续,因为可能会发生虚假唤醒。 watch more here

    pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
    pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
    int ready = 0;
    void* T1(){
        printf("thread 1 started\n");
    
        while(1)
        {
            pthread_mutex_lock(&lock);
            while(ready == 1){
                pthread_cond_wait(&cond,&lock);
            }
            printf("ping\n");
            ready = 1;
            pthread_cond_signal(&cond);
            pthread_mutex_unlock(&lock);
        }
    }
    
    void* T2(){
        printf("thread 2 started\n");
    
        while(1)
        {
            pthread_mutex_lock(&lock);
            while(ready == 0){
                pthread_cond_wait(&cond,&lock);
            }
            printf("pong\n");
            ready = 0;
            pthread_cond_signal(&cond);
            pthread_mutex_unlock(&lock);
        }
    }
    
    int main(void)
    {
        int i = 1;
        pthread_t t1;
        pthread_t t2;
    
    
    
        printf("main\n");
        pthread_create(&t1,NULL,&T1,NULL);
        pthread_create(&t2,NULL,&T2,NULL);
        pthread_join(t1,NULL);
        pthread_join(t2,NULL);
        return EXIT_SUCCESS;
    }
    

    您还应该在main 中使用pthread_join() 而不是while(1)

    【讨论】:

      猜你喜欢
      • 2012-12-14
      • 1970-01-01
      • 2014-08-02
      • 1970-01-01
      • 2014-12-21
      • 1970-01-01
      • 2016-06-26
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多