在多线程编程中可能会碰到pthread_cond_signal和pthread_cond_wait使用不当而带来的诡异问题。今天说下我碰到的问题及解决思路,希望能对遇到类似问题的朋友有所帮助。
场景
场景如上图所示,场景说明如下:
首先启动ProcThread,启动后会创建对应的的ProcThreadQueue(先进先出)。并检测队列如果为空则执行pthread_cond_wait()等待被唤醒,消息出队,处理。伪代码如下:
void proc_thread(void)
{
pthread_mutext_lock(&mutex);
while(queue.empty())
{
pthread_cond_wait(&cond);
pthread_mutext_lock(&mutex);
queue.dequeue();
....
pthread_mutex_unlock(&mutex);
}
}
然后启动Thread1,Thread2和Thread3,这3个线程都是从socket收消息,然后对ProcThreadQueue加锁,入队,解锁,然后唤醒ProcThread。伪代码如下:
void recv_thead()
{
while(1)
{
....
pthead_mutex_lock(&mutex);
queue.enque();
pthread_mutex_unlock(&mutex);
pthread_cond_signal(&cond);
}
}
问题
在程序运行过程中出现发送消息方,收到相应消息比较慢导致出现超时错误。
抓包分析发现接收线程入队的消息,有时会需要大概25-30s左右才能被ProcThread接受处理,从而导致出现超时错误。
思路1:ProcThread没有被及时唤醒,导致消息处理慢,
验证1:将pthead_cond_signal换成pthread_cond_broadcast,问题依然没解决。
思路2:ProcThread被唤醒,线程锁阻塞,导致处理消息时间变长。
验证2:将pthread_cond_signal(&cond)放在解锁之前,先通知ProcThread,然后再释放线程锁,问题得到解决。
伪代码如下:
void recv_thead()
{
while(1)
{
....
pthead_mutex_lock(&mutex);
queue.enque();
pthread_cond_signal(&cond); //先通知线程等待
pthread_mutex_unlock(&mutex);
}
}
后续
- 网上有文章分析说先通知线程会有性能问题,这个没有验证。如果对消息处理时间没有要求的场景,可以考虑放在解锁之后。
- 线程锁阻塞导致处理消息等待25-30s,这个时间感觉时间比较长。后续有机会找方法会继续验证这个时间为什么会如此长。