文章目录
- 前言
- 一、条件变量概念
- 二、条件变量相关的函数
- 三、条件变量模拟生产者消费者模型
- 四、使用条件变量的好处
- 总结
前言
本篇文章来讲解一下条件变量的使用。
一、条件变量概念
条件变量(Condition Variable)是并发编程中一种线程同步机制,用于实现线程之间的等待和通知机制。它是一种与特定条件相关的线程同步原语。
条件变量用于线程间的协调,允许一个线程在满足某个特定条件之前等待,并在其他线程满足条件后被通知继续执行。它通常与互斥锁(Mutex)结合使用,以提供更精细的线程同步和共享数据的访问控制。
条件变量的基本概念包括以下几个要素:
1.等待和通知:
条件变量提供了等待和通知的机制,等待(Wait)操作用于使线程进入等待状态,直到满足某个特定条件。通知(Signal或Broadcast)操作用于唤醒等待中的线程,告知它们条件已经满足。
2.互斥锁:
在使用条件变量之前,通常需要先获取与之配套的互斥锁,以确保共享数据的互斥访问。互斥锁用于保护与条件相关的共享资源,以防止多个线程同时访问。
3.条件谓词:
条件变量通常与条件谓词(Condition Predicate)一起使用,条件谓词是描述条件是否满足的谓词表达式。在线程等待之前和通知之后,都需要通过条件谓词进行条件的检查。
条件变量的典型用法包括以下步骤:
1.线程获取互斥锁,锁定共享资源。
2.检查条件谓词,判断是否满足等待条件,如果不满足则等待条件变量。
3.线程释放互斥锁并进入等待状态,直到其他线程发出通知。
4.当其他线程满足条件变量的条件并发出通知时,等待中的线程被唤醒。
5.线程重新获取互斥锁,继续执行后续操作。
条件变量的使用可以避免线程的忙等待,提高程序的效率,并且更加灵活地实现线程间的通信和同步。但在使用条件变量时需要注意正确的加锁和解锁的顺序,以避免死锁和竞态条件等并发编程的常见问题。
二、条件变量相关的函数
条件变量在Linux下使用一些常见的函数来实现线程之间的同步和通信。下面是条件变量使用的几个常用函数:
1.pthread_cond_init():用于初始化条件变量。‘
int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr);
参数 cond:指向要初始化的条件变量。
参数 attr:指向条件变量的属性对象,通常传递 NULL。
返回值:成功返回0,失败返回错误码。
2.pthread_cond_destroy():用于销毁条件变量。
int pthread_cond_destroy(pthread_cond_t *cond);
参数 cond:指向要销毁的条件变量。
返回值:成功返回0,失败返回错误码。
3.pthread_cond_wait():用于等待条件变量满足条件。
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
参数 cond:指向要等待的条件变量。
参数 mutex:指向与条件变量关联的互斥锁。
返回值:成功返回0,失败返回错误码。
在调用 pthread_cond_wait() 之前,必须先获得与条件变量关联的互斥锁 mutex 的锁,然后该函数会自动释放 mutex 的锁,并让线程进入等待状态,直到被另一个线程通过 pthread_cond_signal() 或 pthread_cond_broadcast() 唤醒。
4.pthread_cond_signal():用于唤醒一个正在等待条件变量的线程。
int pthread_cond_signal(pthread_cond_t *cond);
参数 cond:指向要唤醒的条件变量。
返回值:成功返回0,失败返回错误码。
pthread_cond_signal() 会唤醒等待 cond 条件变量的一个线程,如果没有线程在等待条件变量,则该函数调用没有任何效果。
5.pthread_cond_broadcast():用于唤醒所有正在等待条件变量的线程。
int pthread_cond_broadcast(pthread_cond_t *cond);
参数 cond:指向要广播的条件变量。
返回值:成功返回0,失败返回错误码。
pthread_cond_broadcast() 会唤醒等待 cond 条件变量的所有线程,即广播唤醒所有等待的线程。
三、条件变量模拟生产者消费者模型
示例代码:
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>
#define BUFFER_SIZE 10
typedef struct {
int buffer[BUFFER_SIZE];
int count;
int in;
int out;
pthread_mutex_t mutex;
pthread_cond_t cond;
} Buffer;
Buffer buffer;
void* producer(void* arg)
{
int item = 1;
while (1)
{
pthread_mutex_lock(&buffer.mutex);
// 检查缓冲区是否已满
while (buffer.count == BUFFER_SIZE)
{
pthread_cond_wait(&buffer.cond, &buffer.mutex);
}
// 写入数据并更新缓冲区状态
buffer.buffer[buffer.in] = item;
buffer.in = (buffer.in + 1) % BUFFER_SIZE;
buffer.count++;
printf("生产者生产了:%d\n", item);
// 唤醒等待的消费者线程
pthread_cond_signal(&buffer.cond);
pthread_mutex_unlock(&buffer.mutex);
item++;
sleep(1);
}
return NULL;
}
void* consumer(void* arg)
{
while (1)
{
pthread_mutex_lock(&buffer.mutex);
// 检查缓冲区是否为空
while (buffer.count == 0)
{
pthread_cond_wait(&buffer.cond, &buffer.mutex);
}
// 读取数据并更新缓冲区状态
int item = buffer.buffer[buffer.out];
buffer.out = (buffer.out + 1) % BUFFER_SIZE;
buffer.count--;
printf("消费者消费了:%d\n", item);
// 唤醒等待的生产者线程
pthread_cond_signal(&buffer.cond);
pthread_mutex_unlock(&buffer.mutex);
sleep(1);
}
return NULL;
}
int main(void)
{
pthread_t producer_thread, consumer_thread;
// 初始化缓冲区和相关的同步对象
buffer.count = 0;
buffer.in = 0;
buffer.out = 0;
pthread_mutex_init(&buffer.mutex, NULL);
pthread_cond_init(&buffer.cond, NULL);
// 创建生产者线程和消费者线程
pthread_create(&producer_thread, NULL, producer, NULL);
pthread_create(&consumer_thread, NULL, consumer, NULL);
// 等待线程结束
pthread_join(producer_thread, NULL);
pthread_join(consumer_thread, NULL);
// 销毁同步对象
pthread_mutex_destroy(&buffer.mutex);
pthread_cond_destroy(&buffer.cond);
return 0;
}
运行效果:
四、使用条件变量的好处
1.避免资源浪费:当条件不满足时,线程可以调用pthread_cond_wait函数来等待条件满足。在等待期间,该线程会释放持有的锁,允许其他线程获得锁并继续执行临界区代码。这样可以避免空转和资源浪费。
2.避免竞争条件:条件变量的使用可以帮助避免竞争条件的发生。线程间的协作通过条件变量来实现,确保在满足特定条件之前,线程不会执行关键代码段。
3.提高并发性:条件变量使得线程能够在需要等待条件满足时休眠,而不是通过忙等待消耗处理器资源。这样可以提高系统的并发性和效率。
4.简化同步逻辑:使用条件变量可以简化同步逻辑,使代码更加清晰易懂。条件变量提供了一种灵活而有效的机制来控制线程的行为,使得编写正确的多线程代码变得更加容易。
总结
本篇文章主要讲解了条件变量的概念和条件变量的相关函数和使用条件变量模拟生产者消费者模型。