分类: LINUX
2015-03-22 14:43:32
使用上pthread_cond_t遇到的死锁问题
最近在一个项目中使用pthread_cond_t的时遇到一个死锁的问题,特记录分享一下。这个问题的使用场景很简单,客户端程序起两个线程,一个线程发送数据给服务器,另一个线程接收服务器的返回。发送线程向服务器发送一个数据报,然后等待服务器返回(用pthread_cond_t等待),然后继续发送下一个数据包……,如此循环下去。发送代码抽取出来的逻辑像下面这样:
static void* thread_fun_send(void* nouse)
{
int send_index = 0;
while(1)
{
//模拟发送数据的socket系统调用
printf("send %d\n",send_index++);
//通知接收线程可以接收数据
pthread_mutex_lock(&mutex_send);
pthread_cond_signal(&cond_send);
pthread_mutex_unlock(&mutex_send);
//等待收到数据的pthread_cond_t
pthread_mutex_lock(&mutex_recv);
pthread_cond_wait(&cond_recv, &mutex_recv);
pthread_mutex_unlock(&mutex_recv);
}
}
接收线程等待服务器的返回,收到服务器返回后给发送线程发信号(用pthread_cond_t发信号),然后继续等待接收服务器下一个返回……,如此循环下去。接收代码抽取出来的逻辑像下面这样:
static void* thread_fun_recv(void* nouse)
{
int recv_index = 0;
while(1)
{
//等待收到数据的pthread_cond_t,模拟阻塞的socket
pthread_mutex_lock(&mutex_send);
pthread_cond_wait(&cond_send, &mutex_send);
pthread_mutex_unlock(&mutex_send);
//模拟接收数据的socket系统调用
printf("recv %d\n",recv_index++);
//通知发送线程数据已经收到
pthread_mutex_lock(&mutex_recv);
pthread_cond_signal(&cond_recv);
pthread_mutex_unlock(&mutex_recv);
}
}
就这么一个简单的应用场景,看起来好像是很合乎逻辑的,但在实际的运行中会出现死锁的问题。经过综合分析后发现其实是在理解pthread_cond_t上不准确了,一开始会认为它和windows中的Event是一样的,只是调用的API函数名不同而已,其实它们是不同的。且不说自动重置这个功能,在信号的处理上它们也是完全不同的。在Windows中,当Event被置为有信号时,内核会检查有没有在等待这个信号的线程,如果有则唤醒它,如果没有信号会保存起来,下一个等待此信号的线程进来时会直接获得信号而继续运行;但在Linux中, 如果pthread_cond_t被置为有信号时,内核也是会检查有没有在等待这个信号的线程,如果有则唤醒它,但如果没有等待的线程内核则不会保存这个信号(好像这个信号被丢掉了一样),下一个等待信号的线程进来时是不会获得信号而只会进入睡眠的,只有在睡眠后下一次pthread_cond_t被置为有信号时,这个线程才会被唤醒。
搞清楚了pthread_cond_t和Event的逻辑之后,对于上面这个客户端的业务逻辑,用最小的改动方式就是把使用pthread_cond_t改为sem_t问题就解决了。
附:测试代码,用下面命令即可编译
gcc -o test_cond test_cond.c -pthread
点击(此处)折叠或打开