gpt4 book ai didi

c++ - pthread_cond_wait/signal 和 mutex 未按预期工作

转载 作者:太空宇宙 更新时间:2023-11-04 05:00:12 25 4
gpt4 key购买 nike

我正在尝试学习 pthread/mutex,但是尽管在网上进行了大量研究/阅读,我还是无法理解这段代码出了什么问题:

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>

struct data
{
int Counter = 0;
int calls = -1;
int iteration = -1;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t condition = PTHREAD_COND_INITIALIZER;
};

void* threadAlarm (void* arg);
void* threadCounter (void* arg);

int main (void)
{
pthread_t monThreadCounter;
pthread_t monThreadAlarm;

struct data mydata;

if (pthread_create (&monThreadAlarm, NULL, threadAlarm,(void*)&mydata)>0)
printf("Pthread Alarme error\n");
if (pthread_create (&monThreadCounter, NULL, threadCounter, (void*)&mydata)>0)
printf("Pthread Counter error\n");

pthread_join (monThreadCounter, NULL);
pthread_join (monThreadAlarm, NULL);

return 0;
}

void* threadCounter (void *arg)
{
struct data *myarg = (struct data *)arg;
srand(time(NULL));

pthread_mutex_lock (&myarg->mutex);

while(1)
{
myarg->Counter += rand()%10; /* We add a random number to the counter */

if(myarg->Counter > 20) /* If Counter is greater than 20, we should trigger the alarm*/
{
myarg->iteration += 1; /* Iteration counter, to check any shift between expected triggers and reality */

printf("Counter = %i(%i)-->",myarg->Counter,myarg->iteration);

pthread_mutex_unlock (&myarg->mutex); /* Unlock mutex before sending signal */

if (pthread_cond_signal (&myarg->condition) >0)
{
printf("COND SIGNAL ERROR\n");
pthread_exit(NULL);
}

usleep(10000); /* The shorter the sleep is, the weirder the output is */

pthread_mutex_lock (&myarg->mutex); /* We should get the lock again before testing/modifying any shared variable */
}
}
}

void* threadAlarm (void* arg)
{
struct data *myarg = (struct data *)arg;

while(1)
{
pthread_mutex_lock(&myarg->mutex);

//while(myarg->Counter<21) // Uneeded? Since we'll never get the lock before the Counter thread detects condition and release it
{
printf("\nWAITING for trigger...\n",myarg->Counter);
if (pthread_cond_wait (&myarg->condition, &myarg->mutex)>0)
{
printf("ERROR COND WAIT\n");
pthread_exit(NULL);
}
}

myarg->calls+=1; // Calls counter, should be equal to iteration counter, overwise calls have been missed

printf("ALARM TRIGGERED! Call #%i/Iteration #%i -> COUNTER RESET\n",myarg->calls, myarg->iteration);

// Counter reset
myarg->Counter = 0;

pthread_mutex_unlock(&myarg->mutex);
}
}

这段代码应该有一个线程将计数器递增一个随机值,直到它大于 20,然后触发另一个等待线程的条件,该线程应该显示一条消息并重置计数器。等等。

我不明白的是,尽管我认为我正在使用互斥体、pthread_cond_wait 和 pthread_cond_signal(如网络上的各种示例中所述),但如果我不引入 usleep 来减慢速度,它的行为就不会按预期进行。

使用usleep(10000),我得到了预期的输出:

WAITING for trigger...
Counter = 23(59)-->ALARM TRIGGERED! Call #59/Iteration #59 -> COUNTER RESET

WAITING for trigger...
Counter = 23(60)-->ALARM TRIGGERED! Call #60/Iteration #60 -> COUNTER RESET

WAITING for trigger...
Counter = 21(61)-->ALARM TRIGGERED! Call #61/Iteration #61 -> COUNTER RESET

调用/迭代计数器是同步的,证明每次达到条件时,“警报”线程都会被正确触发。

但是,如果我减少 sleep ,结果就会变得很奇怪。根本没有 sleep (已注释掉),我得到例如:

WAITING for trigger...
Counter = 21(57916)-->Counter = 23(57917)-->Counter = 29(57918)-->Counter = 38(57919)-->Counter = 45(57920)-->Counter = 45(57921)-->Counter = 45(57922)-->Counter = 49(57923)-->Counter = 52(57924)-->Counter = 55(57925)-->Counter = 61(57926)-->Counter = 65(57927)-->Counter = 70(57928)-->Counter = 77(57929)-->Counter = 83(57930)-->Counter = 86(57931)-->Counter = 92(57932)-->Counter = 95(57933)-->Counter = 99(57934)-->Counter = 107(57935)-->ALARM TRIGGERED! Call #4665/Iteration #57935 -> COUNTER RESET

WAITING for trigger...
Counter = 24(57936)-->Counter = 28(57937)-->Counter = 31(57938)-->Counter = 31(57939)-->Counter = 36(57940)-->Counter = 41(57941)-->Counter = 45(57942)-->Counter = 47(57943)-->Counter = 54(57944)-->Counter = 54(57945)-->Counter = 56(57946)-->Counter = 62(57947)-->Counter = 64(57948)-->Counter = 66(57949)-->Counter = 66

...

尽管计数器已达到触发状态,但它似乎没有触发警报线程并继续增加,并且调用/迭代计数器完全不同步,证明有大量调用被错过。

如何确保每次发出 pthread_cond_signal 时都会真正触发等待线程,并且调用线程将等待,直到触发线程释放互斥体?

如果重要的话,我目前正在 Linux Ubuntu 上编码。

感谢您的帮助。

最佳答案

这是预期的行为。一旦您向条件变量发出信号,等待线程最终将醒来并争夺互斥体,但不能保证发信号线程在此之前无法重新获取互斥体。

如果您希望计数器线程等待警报被消耗,您需要实际对其进行编程来执行此操作。您可以以相反的方式使用相同的条件变量 - 在计数器线程中:

if (pthread_cond_signal (&myarg->condition) >0)
{
printf("COND SIGNAL ERROR\n");
pthread_exit(NULL);
}

pthread_mutex_lock (&myarg->mutex); /* We should get the lock again before testing/modifying any shared variable */

/* Wait for alarm to happen */
while (myarg->calls < myarg->iteration)
{
pthread_cond_wait(&myarg->condition, &myarg->mutex);
}

并在警报线程中,调用 pthread_cond_signal(&myarg->condition)在增加 myarg->calls 之后的某个时刻.

<小时/>

顺便说一句,您确实需要 while(myarg->Counter<21)您已在警报线程中注释掉。考虑以下两种情况:

  1. 警报线程被阻塞在 pthread_mutex_lock()在其主循环的开始。计数器线程具有互斥体并且刚刚递增 myarg->Counter为大于 20 的值。它会在警报线程有机会运行之前解锁互斥体并向条件变量发出信号。然后警报线程运行,获取互斥体并阻止 pthread_cond_wait() - 它将永远在这里等待,因为我们现在已经确保计数器线程将等待警报被消耗后再继续。

  2. 警报线程刚刚将计数器递减至零,解锁互斥体,立即在循环顶部重新锁定它并调用 pthread_cond_wait()pthread_cond_wait()在计数器线程有机会获取互斥锁之前立即返回(由于“虚假唤醒”,这是允许的),并且警报线程现在将继续,即使计数器仍然为零。

关于c++ - pthread_cond_wait/signal 和 mutex 未按预期工作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35565946/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com