跳到主要内容

条件变量

1. 基本概念

条件变量是一种同步机制,它能够让一个线程等待另一个线程满足特定的条件。条件变量通常与互斥量配合使用,用于实现线程同步和通信。

在 Linux 系统中,条件变量是通过 pthread_cond_t 类型来表示的。

对于条件变量,一般来说,操作步骤如下:

  1. 调用 pthread_cond_init() 函数初始化条件变量。
  2. 获取互斥量。
  3. 检查某个条件是否满足。
  4. 如果满足,则执行相应的操作。
  5. 如果不满足,则调用 pthread_cond_wait() 函数等待条件变量。
  6. 当收到通知后,继续检查条件是否满足,并重复以上操作。
  7. 释放互斥量。
  8. 调用 pthread_cond_destroy() 函数销毁条件变量。

2. 条件变量API

2.1 定义条件变量并初始化

pthread_cond_t cond = PTHREAD_COND_INITIALIZER;		// 定义条件变量

这种方法等价于使用 pthread_cond_init() 函数初始化条件变量,但是更加简洁。

注意:使用PTHREAD_COND_INITIALIZER宏初始化条件变量时,需要保证该读写锁变量是全局变量或者静态变量。

2.2 初始化条件变量

pthread_cond_init() 函数用于初始化条件变量。函数原型为:

int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr);

该函数接受两个参数:

  • cond:指向条件变量的指针。
  • attr:指向条件变量属性的指针。

如果初始化成功,则该函数返回 0。否则,返回一个非 0 值。

注意:在调用该函数时,条件变量默认处于未被通知的状态。在初始化后,线程可以通过调用 pthread_cond_wait() 函数等待条件变量的通知。另一个线程可以调用 pthread_cond_signal() 函数来通知条件变量。

2.3 销毁条件变量

pthread_cond_destroy() 函数用于销毁条件变量。函数原型为:

int pthread_cond_destroy(pthread_cond_t *cond);

该函数接受一个参数:

  • cond:指向条件变量的指针。

如果销毁成功,则该函数返回 0。否则,返回一个非 0 值。

注意:调用 pthread_cond_destroy() 函数后,条件变量将不能再使用,如果还有线程等待该条件变量,则会收到一个错误。因此,通常在销毁条件变量前,应该先通知所有等待该条件变量的线程,然后再调用该函数。

2.4 等待队列

pthread_cond_wait() 函数用于让线程等待条件变量的通知。当线程调用该函数时,它会释放关联的互斥锁,然后阻塞,直到另一个线程调用 pthread_cond_signal()pthread_cond_broadcast() 函数来通知该线程。当线程被唤醒时,它会再次获得互斥锁。

函数原型为:

int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);

该函数接受两个参数:

  • cond:指向条件变量的指针。
  • mutex:指向关联的互斥锁的指针。

如果线程被成功唤醒,则该函数返回 0。如果发生错误,则返回一个非 0 值。

注意:在调用该函数时,线程必须已经获得了关联的互斥锁。否则,程序的行为是未定义的。

2.5 唤醒条件队列(单个)

pthread_cond_signal() 函数用于通知条件变量,唤醒正在等待该条件变量的线程。函数原型为:

int pthread_cond_signal(pthread_cond_t *cond);

该函数接受一个参数:

  • cond:指向条件变量的指针。

如果唤醒操作成功,则该函数返回 0。如果发生错误,则返回一个非 0 值。

注意:该函数仅会唤醒一个正在等待条件变量的线程。如果有多个线程正在等待条件变量,则必须调用该函数多次才能唤醒所有线程。另一个选项是使用 pthread_cond_broadcast() 函数,该函数可以一次性唤醒所有正在等待条件变量的线程。

2.6 唤醒条件队列(全部)

pthread_cond_broadcast() 函数用于通知条件变量,唤醒所有正在等待该条件变量的线程。函数原型为:

int pthread_cond_broadcast(pthread_cond_t *cond);

该函数接受一个参数:

  • cond:指向条件变量的指针。

如果唤醒操作成功,则该函数返回 0。如果发生错误,则返回一个非 0 值。

注意:该函数会一次性唤醒所有正在等待条件变量的线程。如果只想唤醒一个线程,则可以使用 pthread_cond_signal() 函数。

3. 示例代码

下面这段代码创建了两个线程,thread1thread2,然后等待它们完成。thread1 等待一个条件变量,直到 thread2 向它发出信号。当 thread1 收到信号时,它会打印一条消息,然后退出。thread2 也会打印一条消息并退出。

代码使用互斥锁和条件变量来同步两个线程。互斥锁用于保护对共享变量 flag 的访问,条件变量用于等待 thread2 设置 flag。当 thread2 设置了 flag,它会发出信号,唤醒 thread1 继续执行。

#include <pthread.h>
#include <stdio.h>

pthread_mutex_t mutex;
pthread_cond_t cond;
int flag = 0;

void *thread1(void *arg)
{
pthread_mutex_lock(&mutex); // 获取互斥锁
while (flag == 0)
pthread_cond_wait(&cond, &mutex); // 等待条件变量
pthread_mutex_unlock(&mutex); // 释放互斥锁

printf("Thread 1\n");

pthread_exit(NULL);
}

void *thread2(void *arg)
{
pthread_mutex_lock(&mutex); // 获取互斥锁
flag = 1;
pthread_cond_signal(&cond); // 发送信号
pthread_mutex_unlock(&mutex); // 释放互斥锁

printf("Thread 2\n");

pthread_exit(NULL);
}

int main(int argc, char *argv[])
{
pthread_mutex_init(&mutex, NULL); // 初始化互斥锁
pthread_cond_init(&cond, NULL); // 初始化条件变量

pthread_t t1, t2;
pthread_create(&t1, NULL, thread1, NULL);
pthread_create(&t2, NULL, thread2, NULL);

pthread_join(t1, NULL);
pthread_join(t2, NULL);

pthread_cond_destroy(&cond); // 销毁条件变量
pthread_mutex_destroy(&mutex); // 销毁互斥锁

return 0;
}