我猜测您可能对
pthread_cond_broadcast
的工作方式有误解(至少是我过去遇到的问题之一,但不确定是否适用于您的情况)。来自
man page的描述如下:
pthread_cond_broadcast()
函数将解除所有当前阻塞在指定条件变量cond上的线程。
好的,这很有道理,
_broadcast
唤醒了所有当前阻塞在
cond
上的线程。然而,只有一个被唤醒的线程能够在它们全部唤醒后锁定互斥锁。同样来自man page的描述:
已解除阻塞的线程应根据调度策略(如果适用)争夺互斥锁,并且就像每个线程都调用了pthread_mutex_lock()
一样。
这意味着如果有3个线程被阻塞在
cond
上并且调用了
_broadcast
,那么所有3个线程都会被唤醒,但只有1个线程能够获取互斥锁。其他2个线程仍然被阻塞在
pthread_cond_wait
上,等待信号。由于这个原因,它们没有看到
stop
被设置为1,并且
exit_handler
(我假设是一个Ctrl+c软件信号?)已经完成了信号处理,所以输掉了
_broadcast
竞赛的剩余线程陷入了僵局,等待永远不会到来的信号,并且无法读取已经设置的
stop
标志。
我认为有两个解决方法:
- 使用
pthread_cond_timedwait
。即使没有被信号唤醒,也将在指定的时间间隔内从等待中返回,查看stop == 1
,然后退出。
- 在您的
worker
函数末尾添加pthread_cond_signal
或pthread_cond_broadcast
。这样,在线程退出之前,它将发出cond
变量的信号,允许任何其他等待线程获取互斥锁并完成处理。如果没有线程在等待条件变量,发出条件变量信号也没有问题,因此即使是最后一个线程,这也应该没问题。
编辑: 这里有一个最小可复现示例,证明了我上面的答案是错误的。当我按下Ctrl + c时,程序会立即退出,这表明所有线程在广播后很快获取了互斥锁,看到 stop
为false,然后退出。然后main
加入线程并结束进程。
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <stdbool.h>
#include <signal.h>
#include <unistd.h>
#define NUM_THREADS 3
#define STACK_SIZE 10
pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t c = PTHREAD_COND_INITIALIZER;
volatile bool stop = false;
int stack[STACK_SIZE] = { 0 };
int sp = 0;
void SigHandler(int sig)
{
if (sig == SIGINT)
{
stop = true;
}
else
{
printf("Received unexcepted signal %d\n", sig);
}
}
void* worker(void* param)
{
long tid = (long)(param);
while (stop == false)
{
pthread_mutex_lock(&m);
while (sp <= 0)
{
pthread_cond_wait(&c, &m);
}
printf("thread %ld consuming stack[%d] = %d\n", tid, sp-1, stack[sp-1]);
sp--;
pthread_mutex_unlock(&m);
int sleepVal = rand() % 10;
printf("thread %ld sleeping for %d seconds...\n", tid, sleepVal);
sleep(sleepVal);
}
pthread_exit(NULL);
}
int main(void)
{
pthread_t threads[NUM_THREADS];
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
srand(time(NULL));
for (long i=0; i<NUM_THREADS; i++)
{
int rc = pthread_create(&threads[i], &attr, worker, (void*)i);
if (rc != 0)
{
fprintf(stderr, "Failed to create thread %ld\n", i);
}
}
while (stop == false)
{
int numValsToInsert = rand() % (STACK_SIZE - sp);
printf("main producing %d values\n", numValsToInsert);
pthread_mutex_lock(&m);
for (int i=0; i<numValsToInsert; i++)
{
int val = rand() % 10000;
if (sp+1 < STACK_SIZE)
{
printf("main pushing stack[%d] = %d\n", sp, val);
stack[sp++] = val;
}
else
{
printf("stack full!\n");
}
}
pthread_mutex_unlock(&m);
printf("main signaling threads...\n");
pthread_cond_broadcast(&c);
int sleepVal = 1;
printf("main sleeping for %d seconds...\n", sleepVal);
sleep(sleepVal);
}
for (long i=0; i<NUM_THREADS; i++)
{
pthread_join(threads[i], NULL);
}
return 0;
}
stop = 1
应该在互斥保护的区域内执行。这是一条注释,因为我看不出它如何引起你所看到的问题。 - cafphtread_cond_signal
而不是pthread_cond_broadcast
)。我编写了一些测试代码,我认为它做了你描述的事情,但我不认为我看到了你的问题。 - yano