实验结论:
1 同一时间,只有1个线程可以获得互斥锁
2 使用默认互斥锁属性 互斥锁不可重入
3 初始化条件变量后 调用pthread_cond_wait 线程状态为S 睡眠,且释放了互斥锁
4 当线程队列中只有1条处于wait状态的线程,调用pthread_cond_signal,可以将其唤醒
5 唤醒后pthread_cond_wait不是立即返回,而是获得互斥锁后才返回
6 同理循环使用pthread_cond_signal可以随机唤醒多条等待线程
7 使用pthread_cond_broadcast可以唤醒全部等待线程
8 使用条件变量必须使用互斥锁
int pthread_cond_wait (pthread_cond_t *__restrict __cond,pthread_mutex_t *__restrict __mutex)
__nonnull ((1, 2));//表示参数1,2不可以为null
9 必须使用互斥锁保护条件变量,以下是一个未保护的情形:
消费者消费后发现仓库为空,将要调用pthread_cond_wait之际,系统调度到生产者线程
生产满了之后,pthread_cond_signal 消费者 因为条件变量没有受到同步保护所以先收到了signal
后调用了 wait 导致无限期休眠
实验描述:
创建两个线程 一个发送signal 一个wait
wait先走,来到等待点,睡眠
三秒后发送条件变量信号,发现wait并没有返回,因为signal还没有释放锁
三秒后,signal释放锁,wait返回成功,全剧终
全程使用互斥锁保护条件变量
实验环境:
unix-like系统 gnu_c 或其他支持posix的系统,可在IDE下运行,也可单独编译运行
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdio.h>
#include <pthread.h>
pthread_cond_t cond;
pthread_mutex_t mutexlock;
void *cv_wait(void *p)
{
// 因为cv_signal上来就睡,所以cv_wait先锁
pthread_mutex_lock(&mutexlock);
printf("%lu pthread_cond_wait\n", pthread_self());
// 等待并释放锁
if (pthread_cond_wait(&cond, &mutexlock) == 0)
{
printf("%lu pthread_cond_wait 返回成功\n", pthread_self());
}
printf("pthread_cond_wait 获得互斥锁后才能返回 不是收到信号立即返回\n");
// 解锁
pthread_mutex_unlock(&mutexlock);
pthread_exit(NULL);
}
void *cv_signal(void *p)
{
pthread_t tid = *((pthread_t *)p);
for (size_t i = 3; i > 0; i--)
{
sleep(1);
printf("About to wake up %lu , %zu\n", tid, i);
}
sleep(1);
// cv_wait线程先拿到锁,cond_wait函数阻塞,并没有走到mutex_unlock函数
// 而后cv_signal 拿到了锁 说明pthread_cond_wait释放了锁
pthread_mutex_lock(&mutexlock);
printf("%lu pthread_cond_signal\n", pthread_self());
// 尝试唤醒 wait状态的cv_wait线程
pthread_cond_signal(&cond);
// pthread_cond_signal(&cond);
// 可以增加线程使用broadcast观察效果
// pthread_cond_broadcast(&cond);
// 如果此处有耗时逻辑 则需要等释放锁后 pthread_cond_wait 才能返回
for (size_t i = 3; i > 0; i--)
{
printf("等%zu秒释放锁\n", i);
sleep(1);
}
// 释放锁后pthread_cond_wait 才能返回
pthread_mutex_unlock(&mutexlock);
pthread_exit(NULL);
}
int main()
{
pthread_mutex_init(&mutexlock, NULL);
if (pthread_cond_init(&cond, NULL))
{
perror("pthread_cond_init");
}
pthread_t tids[3] = {0};
pthread_create(&tids[0], NULL, cv_wait, NULL);
// pthread_create(&tids[2], NULL, cv_wait, NULL);
pthread_create(&tids[1], NULL, cv_signal, (void *)&tids[0]);
pthread_join(tids[0], NULL);
pthread_join(tids[1], NULL);
// pthread_join(tids[2], NULL);
pthread_mutex_destroy(&mutexlock);
pthread_cond_destroy(&cond);
// 进程结束
printf("%lu all done\n", pthread_self());
return 0;
}