在Linux环境下,条件变量(Condition Variables)是一种线程同步机制,允许线程在某个条件未满足时进入等待状态,并在其他线程修改共享资源或条件后通知它们。
条件变量和互斥锁通常一起使用,以保证对共享资源的安全访问。
通过条件变量,线程可以避免忙等待(busy-waiting),从而提高效率。
条件变量使得线程可以通过以下方式同步:
条件变量通常与互斥锁结合使用,因为在检查或修改某些共享资源时,需要保护这些资源的并发访问,防止竞争条件。
互斥锁负责保护共享资源,条件变量负责在线程间传递状态信息。
具体步骤如下:
条件变量的使用步骤:
1
条件变量的初始化和销毁
条件变量使用pthread_cond_t数据类型来表示,和互斥锁类似,条件变量在使用前必须初始化。
可以通过两种方式进行初始化:
1、使用宏PTHREAD_COND_INITIALIZER,类似于互斥锁的静态初始化方式:
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
2、使用pthread_cond_init()函数动态初始化条件变量:
pthread_cond_init(&cond, NULL); // attr 为 NULL 时使用默认属性
参数:
返回值:
当不再使用条件变量时,应使用pthread_cond_destroy()进行销毁,以释放系统资源,销毁前需要确保没有线程在等待该条件变量。
pthread_cond_destroy(&cond);
参数:
返回值:
注意事项:
2
发送信号和等待条件变量
条件变量的核心功能就是发送信号和等待条件。
在多线程程序中,线程通过pthread_cond_wait()等待条件,而其他线程通过pthread_cond_signal()或pthread_cond_broadcast()发出信号。
pthread_cond_wait():使线程等待条件满足,并在等待期间释放互斥锁。该函数会阻塞调用线程,直到另一个线程调用 pthread_cond_signal() 或 pthread_cond_broadcast() 通知该条件变量。
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
参数:
返回值:
注意事项:
pthread_cond_signal():用于通知至少一个等待该条件变量的线程,使其从 pthread_cond_wait() 的阻塞状态中唤醒。如果没有线程在等待条件变量,该信号会被丢弃。
int pthread_cond_signal(pthread_cond_t *cond);
参数:
返回值:
注意事项:
pthread_cond_broadcast():用于唤醒所有等待该条件变量的线程。适用于需要唤醒多个线程的场景。
int pthread_cond_broadcast(pthread_cond_t *cond);
参数:
返回值:
假设有多个消费者线程在等待产品,如果使用pthread_cond_signal(),只有一个线程会被唤醒。
如果我们希望所有消费者都被唤醒,则使用pthread_cond_broadcast()。
但一般来说,pthread_cond_signal()更高效,除非确实需要所有线程都被唤醒。
pthread_cond_signal(&cond); // 唤醒一个线程
pthread_cond_broadcast(&cond); // 唤醒所有等待的线程
3
条件变量中的判断条件
在使用条件变量时,通常涉及某个共享变量(如上例中的buffer)。
在检测条件时,必须使用while循环而非if语句。这是因为:
这样设计能确保线程在条件不满足时不会继续执行,从而避免竞争条件带来的问题。
pthread_mutex_lock(&mutex);
while (buffer == 0) {
pthread_cond_wait(&cond, &mutex);
}
以下示例展示了生产者-消费者模型,其中生产者线程和消费者线程通过条件变量进行同步:
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
int buffer = 0; // 缓冲区,0表示空,1表示有产品
void *producer(void *arg) {
pthread_mutex_lock(&mutex); // 加锁
while (buffer == 1) {
pthread_cond_wait(&cond, &mutex); // 缓冲区满,等待
}
buffer = 1; // 生产产品
printf("Produced an item\n");
pthread_cond_signal(&cond); // 通知消费者
pthread_mutex_unlock(&mutex); // 解锁
return NULL;
}
void *consumer(void *arg) {
pthread_mutex_lock(&mutex); // 加锁
while (buffer == 0) {
pthread_cond_wait(&cond, &mutex); // 缓冲区空,等待
}
buffer = 0; // 消费产品
printf("Consumed an item\n");
pthread_cond_signal(&cond); // 通知生产者
pthread_mutex_unlock(&mutex); // 解锁
return NULL;
}
int main() {
pthread_t prod, cons;
// 创建生产者和消费者线程
pthread_create(&prod, NULL, producer, NULL);
pthread_create(&cons, NULL, consumer, NULL);
// 等待线程执行完毕
pthread_join(prod, NULL);
pthread_join(cons, NULL);
// 销毁互斥锁和条件变量
pthread_mutex_destroy(&mutex);
pthread_cond_destroy(&cond);
return 0;
}
Linux中的条件变量是线程同步的强大工具,允许线程等待特定条件满足后再执行操作,避免了无效的忙等待。
通过与互斥锁协作,条件变量可以有效地协调线程之间对共享资源的访问,保证并发环境下的安全性和效率。