#C 语言标准库函数 cnd_wait
/*********************************************
* @brief 等待条件变量上收到信号
* @param cond 要等待的条件变量 ID
* @param mtx 要解锁的互斥量 ID
* @return 是否成功
********************************************/
int cnd_wait(cnd_t* cond, mtx_t* mtx);
说明
原子地解锁互斥量 mtx
并阻塞当前线程,直到条件变量 cond
上收到 cnd_signal 或 cnd_broadcast 发送的信号时唤醒线程并重新锁定互斥量 mtx
。
这个函数可能发生虚假的唤醒,即条件变量上并没有收到信号,线程却被唤醒并返回 thrd_success
;这是由于内存(缓存)可见性延迟、硬件优化、内存屏障、线程调度、系统信号等原因导致的正常现象。
因此,标准做法是:
mtx_lock(&mutex); // 锁定条件变量
while (!条件) { // 循环检查条件,唤醒时会再次检查
cnd_wait(&cond, &mutex); // 等待:原子地解锁互斥量并阻塞,唤醒时自动锁定互斥量
}
mtx_unlock(&mutex); // 解锁条件变量
而不是:
mtx_lock(&mutex); // 锁定条件变量
if (!条件) { // 检查条件,唤醒时直接结束
cnd_wait(&cond, &mutex); // 等待:原子地解锁互斥量并阻塞,唤醒时自动锁定互斥量
}
mtx_unlock(&mutex); // 解锁条件变量
// 错误,这里条件不一定成立
// 即使 cnd_signal 或 cnd_broadcast 时正确设置了条件
// 因为可能虚假的唤醒
参数
cond
- 要等待的条件变量 IDmtx
- 要解锁的互斥量 ID
返回值
- 成功时返回
thrd_success
- 失败时返回
thrd_error
#示例
#include <stdio.h>
#include <threads.h>
// 全局变量
mtx_t mutex; // 互斥量
cnd_t cond; // 条件变量
int turn = 1; // 1 表示线程 1 打印,2 表示线程 2 打印
// 线程函数1
int func1(void* data)
{
// 与线程 2 交替打印
for (int i = 0; i < 10; i++)
{
mtx_lock(&mutex); // 锁定互斥量
while (turn != 1)
{
cnd_wait(&cond, &mutex); // 等待信号
}
printf("线程1: %d\n", i);
turn = 2;
cnd_signal(&cond); // 发送信号,唤醒线程 2
mtx_unlock(&mutex); // 解锁互斥量
}
return 0;
}
// 线程函数2
int func2(void* data)
{
// 与线程 1 交替打印
for (int i = 0; i < 10; i++)
{
mtx_lock(&mutex); // 锁定互斥量
while (turn != 2)
{
cnd_wait(&cond, &mutex); // 等待信号
}
printf("线程2: %d\n", i);
turn = 1;
cnd_signal(&cond); // 发送信号,唤醒线程 1
mtx_unlock(&mutex); // 解锁互斥量
}
return 0;
}
int main(void)
{
mtx_init(&mutex, mtx_plain); // 创建互斥量
cnd_init(&cond); // 创建条件变量
// 创建线程
thrd_t th1, th2;
thrd_create(&th1, func1, NULL);
thrd_create(&th2, func2, NULL);
// 等待线程结束
thrd_join(th1, NULL);
thrd_join(th2, NULL);
// 清除互斥量和条件变量
mtx_destroy(&mutex);
cnd_destroy(&cond);
return 0;
}
运行结果:
线程1: 0 线程2: 0 线程1: 1 线程2: 1 线程1: 2 线程2: 2 线程1: 3 线程2: 3 线程1: 4 线程2: 4 线程1: 5 线程2: 5 线程1: 6 线程2: 6 线程1: 7 线程2: 7 线程1: 8 线程2: 8 线程1: 9 线程2: 9
说明:
这个示例通过条件变量实现两个线程交替打印。
#推荐阅读
#参考标准
- C17 standard (ISO/IEC 9899:2018):
- 7.26.3.6 The cnd_wait function (p: 277)
- C11 standard (ISO/IEC 9899:2011):
- 7.26.3.6 The cnd_wait function (p: 380)