【问题标题】:pthread_cond_wait donst wake-up when using loop使用循环时 pthread_cond_wait 不会唤醒
【发布时间】:2022-01-13 03:52:06
【问题描述】:

我是线程新手 我想使用 ptherad_cond_signal 和 pthread_cond_wait 检查一些情况 我有这个代码作为示例:

pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
int x = 0;
void* f1(void *arg){

    for (int i = 0; i < 10; i++)
    {
        pthread_mutex_lock(&lock);
        x += 10;
        printf("%d\n", x);
        pthread_cond_signal(&cond);
        pthread_mutex_unlock(&lock);
    }
    return NULL;
}


void* f2(void* arg){
    pthread_mutex_lock(&lock);
    while (x < 40)
    {
        pthread_cond_wait(&cond, &lock);
    }

    x -= 40;
    pthread_mutex_unlock(&lock);
    return NULL;
}


int main(int argc, char *args[]){    
    pthread_t p1, p2;

    pthread_create(&p2, NULL, f2, NULL);
    pthread_create(&p1, NULL, f1, NULL);    
    
    pthread_exit(NULL);
    
    return 0;
}

结果:

10
20
30
40
50
60
70
80
90
100

但我希望:

10
20
30
40
10
20
30
40
50
60

为什么在 pthread_cond_signal 之后,函数 f2 不继续?
似乎在 f1 for 循环中,在 pthread_cond_wait 唤醒之前再次锁定

【问题讨论】:

  • stackoverflow.com/q/14947191/2864740 - pthread_mutex_lock 不能保证是公平的,创建的顺序也不能保证调度。无论如何,在 f1 解锁后睡一秒钟以获得不同的行为(仍然不正确,但它应该显示原因..)。
  • @user2864740 - 添加睡眠语句以使多线程代码具有预期的行为是一种糟糕的传播模式。它大部分都可以工作,但是在一个复杂的系统上,一个线程“等待足够长的时间”保证另一个线程已经完成它的工作是错误的假设。它打败了条件变量的全部意义。这会导致奇怪的错误。
  • @user2864740 - 感谢您的澄清。

标签: c multithreading pthreads mutex condition-variable


【解决方案1】:

您的第一个问题是main 创建了两个线程,但可能会在其他两个线程完成之前退出(并退出您的程序)。也许 Linux 构建中的线程存在细微差别,在 main 返回时退出进程之前等待所有线程完成。我总是忘记这里的规则,但这是main 等待子线程完成的首选模式。这是一个简单的改变

int main(int argc, char *args[]){    
    pthread_t p1, p2;

    pthread_create(&p2, NULL, f2, NULL);
    pthread_create(&p1, NULL, f1, NULL);    
    
    // wait for both threads to complete
    pthread_join(&p1, NULL);
    pthread_join(&p2, NULL);
    
    return 0;
}

回到你原来的问题。

在上下文切换到 f2 之前,没有什么可以阻止 f1 递增到 100。

听起来你想要的是让f1 将 X 增加到 40,然后等待 f2 将其放回原处,然后再继续将增量间隔增加到 60。

您可以轻松地在两个线程中使用条件变量来获得更改通知并在另一个线程上等待。

我已经重构了你的一些代码,所以我不必一遍又一遍地重写同一个块。但它仍然主要是您的原始代码。

void IncrementX(int val)
{
    pthread_mutex_lock(&lock);
    x += 10;
    printf("%d\n", x);
    pthread_cond_signal(&cond);
    pthread_mutex_unlock(&lock);

}

void WaitForX(int target, int gt)
{
    pthread_mutex_lock(&lock);
    while ((x >= target && gt) || (x < target && !gt))
    {
        pthread_cond_wait(&cond, &lock);
    }
    pthread_mutex_unlock(&lock);    
}

void* f1(void *arg){

    // increment to 40
    for (int i = 0; i < 4; i++)
    {
        IncrementX(10);
    }

    WaitForX(40, 0); // wait for X to drop below 40

    // increment back to 60
    for (int i = 0; i < 6; i++)
    {
        IncrementX(10);
    }

    return NULL;
}

void* f2(void* arg){

    WaitForX(40, 1); // wait for X to go to 40 or above

    IncrementX(-40);

    return NULL;
}

【讨论】:

  • 感谢@selbie,“IncrementX: x+=val, f2: WaitForX(40, 0), f1: WaitForX(40, 1)”,效果很好,但我的主要问题是如何管理上下文切换之前的信号,不使用睡眠或在 f1 中使用“WaitForX”。我希望 f1 在 X 更改后等待,然后第二个线程检查 X 值,然后 f1 继续,这种情况一次又一次地发生,也许我想使用多个线程,我想使用 pthread_cond_broadcast,我不想定义一个指定的指向 f1 等待,我希望 f1 改变 X 后等待,
  • 我用过:pthread_exit(NULL);在 main 函数中,所以我没有使用 join。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2022-01-11
  • 2012-01-25
  • 1970-01-01
  • 2012-10-27
  • 2011-10-11
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多