【发布时间】:2022-08-16 10:42:12
【问题描述】:
我已经阅读了一些建议的主题,但不幸的是还没有找到我的问题的答案。 任何建议都非常感谢。
所以,我正在做这个巨大的项目,这里是影响代码 sn-p:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <time.h>
#include <sys/time.h>
#include <unistd.h>
#include <pthread.h>
static pthread_mutex_t mutex;
static pthread_cond_t cond;
static char *timestamp()
{
time_t rawtime;
struct tm *timeinfo;
char *stamp;
time(&rawtime);
timeinfo = localtime(&rawtime);
stamp = asctime(timeinfo);
stamp[strlen(stamp) - 1] = \'\\0\';
return stamp;
}
static void *monitor(void *ctx)
{
int ret;
struct timeval now;
struct timespec timeout = { 0 };
while (1)
{
gettimeofday(&now, NULL);
timeout.tv_sec = now.tv_sec + 1;
pthread_mutex_lock(&mutex);
if (!(ret = pthread_cond_timedwait(&cond, &mutex, &timeout)))
{
printf(\"%s [%s] Signaled successfully!\\n\", timestamp(),
__FUNCTION__);
}
else if (ret == ETIMEDOUT)
printf(\"%s [%s] Timed out!\\n\", timestamp(), __FUNCTION__);
pthread_mutex_unlock(&mutex);
}
return NULL;
}
int main()
{
pthread_t tid;
if (pthread_mutex_init(&mutex, NULL) < 0)
{
perror(\"mutex\");
return -1;
}
if (pthread_cond_init(&cond, NULL) < 0)
{
perror(\"cond\");
pthread_mutex_destroy(&mutex);
return -1;
}
if (pthread_create(&tid, NULL, monitor, NULL) < 0)
{
perror(\"create()\");
pthread_cond_destroy(&cond);
pthread_mutex_destroy(&mutex);
return -1;
}
pthread_detach(tid);
srand(time(NULL));
while (1)
{
int delay = rand() % 5 + 1;
printf(\"%s [main] Waker is sleeping for %d sec\\n\", timestamp(), delay);
sleep(delay);
printf(\"%s [main] Signaling...\\n\", timestamp());
pthread_mutex_lock(&mutex);
printf(\"%s [main] Lock aquired...\\n\", timestamp());
pthread_cond_signal(&cond);
printf(\"%s [main] Signaled. Releasing...\\n\", timestamp());
pthread_mutex_unlock(&mutex);
}
pthread_cond_destroy(&cond);
pthread_mutex_destroy(&mutex);
return 0;
}
每次当[main] 线程伸出pthread_cond_signal 和pthread_cond_timedwait 正在等待(未超时)时,就会发生卡住。
在pthread_cond_signal 之前锁定互斥锁是最佳实践,我已阅读here
This 主题说,如果 cond/mutex 在等待之前被破坏,则可能会发生这种卡住。
This主题描述虚假的醒来,这可能会导致这种卡住。
但是,两者似乎都与我的情况无关。
我还假设这种行为可能与ulimit -s/-i 有关。但是设置为unlimited 值并没有帮助。
有趣的是[monitor] 线程也像[main] 一样卡住了。
UPD
Program output:
Wed Jun 8 13:34:10 2022 [main] Waker is sleeping for 4 sec
Wed Jun 8 13:34:10 2022 [monitor] Timed out!
Wed Jun 8 13:34:11 2022 [monitor] Timed out!
Wed Jun 8 13:34:12 2022 [monitor] Timed out!
Wed Jun 8 13:34:13 2022 [monitor] Timed out!
Wed Jun 8 13:34:14 2022 [main] Signaling...
Wed Jun 8 13:34:14 2022 [main] Lock acquired...
/*No prints at all */
UPD2:
我重构了上面的例子只是为了像这样使用pthread_cond_wait:
[monitor thread]
pthread_mutex_lock(&mutex);
pthread_cond_wait(&cond, &mutex);
pthread_mutex_unlock(&mutex);
[main thread]
pthread_mutex_lock(&mutex);
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex);
我又挂在pthread_cond_signal上...
因此,从操作系统的角度来看,这似乎是个问题。我只知道小 ulimit 的堆栈大小可能会导致堆栈溢出,这可能会导致这种卡住(依赖于拱门的东西,在我的情况下 100% 可重现)。
有谁知道其他可能会影响它的特定配置?
-
我不确定这是否是您的问题,但
asctime不是线程安全的。它返回一个指向两个线程同时读写的静态缓冲区的指针。您应该改用asctime_r或strftime。 -
还有另一个竞争错误:您的监视器线程在每次循环迭代时解锁并锁定互斥锁,除了它的等待。在这两者之间,主线程可以锁定互斥体并发出条件信号。由于监视器当时没有等待条件,因此它会错过信号,并且后续的等待将永远不会收到它。
-
现在解锁/锁定竞赛并不能解释你的挂起,因为即使监视器错过了一个信号,主线程也会在几秒钟内发送另一个信号。但这是一个设计问题。通常监视器的主循环应该不是有一个明确的解锁/锁定:在任何时候它都应该等待或持有互斥锁。如果它确实解锁了互斥锁,那么在它再次等待之前,它需要确定(通过检查一些其他程序状态)是否已经发生了所需的事件。
-
\“如果当前没有线程阻塞在 cond 上,则 pthread_cond_broadcast() 和 pthread_cond_signal() 函数将无效。\”由于手册页:linux.die.net/man/3/pthread_cond_signal
-
正确的。就像我说的,它没有解释为什么
pthread_cond_signal应该看起来挂起。在这个程序中,唯一的后果是监视器会错过信号并等待比预期更长的时间,但在实际程序中(没有超时)它可能会死锁。我无法重现您描述的挂起,所以我只能猜测。
标签: c linux multithreading pthreads embedded-linux