条件变量
条件变量其实就是一个信号量,用于线程间同步。条件变量用来阻塞一个线程,当条件满足时向阻塞的线程发送一个条件,阻塞线程就被唤醒,条件变量需要和互斥锁配合使用,互斥锁用来保护共享数据。
条件变量可以用来通知共享数据状态。比如一个处理共享资源队列的线程发现队列为空,则此线程只能等待,直到有一个节点被添加到队列中,添加后在发一个条件变量信号激活等待线程。
条件变量的主要操作包括:调用pthread_cond_init()对条件变量初始化,调用 pthread_cond_destroy()销毁一个条件变量,调用pthread_cond_wait()等待一个条件变量,调用pthread_cond_signal()发送一个条件变量。
条件变量控制块
每个条件变量对应一个条件变量控制块,包括对条件变量进行操作的一些信息。初始化一个条件变量前需要先定义一个pthread_cond_t条件变量控制块。pthread_cond_t是pthread_cond结构体类型的重定义,定义在pthread.h头文件里。
- struct pthread_cond
- {
- pthread_condattr_t attr; /* 条件变量属性 */
- struct rt_semaphore sem; /* RT-Thread信号量控制块 */
- };
- typedef struct pthread_cond pthread_cond_t;
- rt_semaphore是RT-Thread内核里定义的一个数据结构,是信号量控制块,
- 定义在rtdef.h头文件里
- struct rt_semaphore
- {
- struct rt_ipc_object parent;/*继承自ipc_object类*/
- rt_uint16_t value; /* 信号量的值 */
- };
初始化条件变量
函数原型
- int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr);
- 参数 描述
- cond 条件变量句柄,不能为NULL
- attr 指向条件变量属性的指针,若为NULL则使用默认属性值
函数返回
初始化成功返回0,参数无效返回EINVAL。
此函数会初始化cond条件变量,并根据attr指向的条件变量属性设置其属性,此函数是对rt_sem_init()函数的一个封装,基于信号量实现。初始化成功后条件变量处于不可用状态。
还可以用宏PTHREAD_COND_INITIALIZER静态初始化一个条件变量,方法: pthread_cond_t cond = PTHREAD_COND_INITIALIZER(结构体常量),等同于调用 pthread_cond_init()时attr指定为NULL。
attr一般设置NULL使用默认值即可,具体会在线程高级编程一章介绍。
销毁条件变量
函数原型
- int pthread_cond_destroy(pthread_cond_t *cond);
- 参数 描述
- cond 条件变量句柄,不能为NULL
函数返回
销毁成功返回0,参数无效返回EINVAL,条件变量正在被使用返回EBUSY。
此函数会销毁cond条件变量,销毁后cond处于未初始化状态。销毁之后条件变量的属性及控制块参数将不在有效,但可以调用pthread_cond_init()或者静态方式重新初始化。
销毁条件变量前需要确定没有线程被阻塞在该条件变量上,也不会等待获取、发信号或者广播。
阻塞方式获取条件变量
函数原型
- int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
- 参数 描述
- cond 条件变量句柄,不能为NULL
- mutex 指向互斥锁控制块的指针,不能为NULL
函数返回
成功获取条件变量返回0,参数无效返回EINVAL。
此函数会以阻塞方式获取cond条件变量。线程等待条件变量前需要先将mutex互斥锁锁住,此函数首先判断条件变量是否可用,如果不可用则初始化一个条件变量,之后解锁mutex互斥锁,然后尝试获取一个信号量,当信号量值大于零时,表明信号量可用,线程将获得信号量,也就获得该条件变量,相应的信号量值会减1。如果信号量的值等于零,表明信号量不可用,线程将阻塞直到信号量可用,之后将对mutex互斥锁再次上锁。
指定阻塞时间获取条件变量
函数原型
- int pthread_cond_timedwait(pthread_cond_t *cond,
- pthread_mutex_t *mutex,
- const struct timespec *abstime);
- 参数 描述
- cond 条件变量句柄,不能为NULL
- mutex 指向互斥锁控制块的指针,不能为NULL
- abstime 指定的等待时间,单位是操作系统时钟节拍(OS Tick)
函数返回
成功获取条件变量返回0,参数无效返回EINVAL,超时返回ETIMEDOUT。
此函数和pthread_cond_wait()函数唯一的差别在于,如果条件变量不可用,线程将被阻塞abstime时长,超时后函数将直接返回ETIMEDOUT错误码,线程将会被唤醒进入就绪态。
发送满足条件信号量
函数原型
- int pthread_cond_signal(pthread_cond_t *cond);
- 参数 描述
- cond 条件变量句柄,不能为NULL
函数返回
只返回0,总是成功。
此函数会发送一个信号且只唤醒一个等待cond条件变量的线程,是对rt_sem_release()函数的封装,也就是发送一个信号量。当信号量的值等于零,并且有线程等待这个信号量时,将唤醒等待在该信号量线程队列中的第一个线程,由它获取信号量。否则将把信号量的值加1。
广播
函数原型
- int pthread_cond_broadcast(pthread_cond_t *cond);
- 参数 描述
- cond 条件变量句柄,不能为NULL
函数返回
广播成功返回0,参数无效返回EINVAL。
调用此函数将唤醒所有等待cond条件变量的线程。
条件变量示例代码
这个程序是一个生产者消费者模型,有一个生产者线程,一个消费者线程,它们拥有相同的优先级。生产者每隔2秒会生产一个数字,放到head指向的链表里面,之后调用pthread_cond_signal()给消费者线程发信号,通知消费者线程链表里面有数据。消费者线程会调用pthread_cond_wait()等待生产者线程发送信号。
- #include <pthread.h>
- #include <unistd.h>
- #include <stdio.h>
- #include <stdlib.h>
- /* 静态方式初始化一个互斥锁和一个条件变量 */
- static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
- static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
- /* 指向线程控制块的指针 */
- static pthread_t tid1;
- static pthread_t tid2;
- /* 函数返回值检查 */
- static void check_result(char* str,int result)
- {
- if (0 == result)
- {
- printf("%s successfully!\n",str);
- }
- else
- {
- printf("%s failed! error code is %d\n",str,result);
- }
- }
- /* 生产者生产的结构体数据,存放在链表里 */
- struct node
- {
- int n_number;
- struct node* n_next;
- };
- struct node* head = NULL; /* 链表头,是共享资源 */
- /* 消费者线程入口函数 */
- static void* consumer(void* parameter)
- {
- struct node* p_node = NULL;
- pthread_mutex_lock(&mutex); /* 对互斥锁上锁 */
- while (1)
- {
- while (head == NULL) /* 判断链表里是否有元素 */
- {
- pthread_cond_wait(&cond,&mutex); /* 尝试获取条件变量 */
- }
- /*
- pthread_cond_wait()会先对mutex解锁,
- 然后阻塞在等待队列,直到获取条件变量被唤醒,
- 被唤醒后,该线程会再次对mutex上锁,成功进入临界区。
- */
- p_node = head; /* 拿到资源 */
- head = head->n_next; /* 头指针指向下一个资源 */
- /* 打印输出 */
- printf("consume %d\n",p_node->n_number);
- free(p_node); /* 拿到资源后释放节点占用的内存 */
- }
- pthread_mutex_unlock(&mutex); /* 释放互斥锁 */
- return 0;
- }
- /* 生产者线程入口函数 */
- static void* product(void* patameter)
- {
- int count = 0;
- struct node *p_node;
- while(1)
- {
- /* 动态分配一块结构体内存 */
- p_node = (struct node*)malloc(sizeof(struct node));
- if (p_node != NULL)
- {
- p_node->n_number = count++;
- pthread_mutex_lock(&mutex); /* 需要操作head这个临界资源,先加锁 */
- p_node->n_next = head;
- head = p_node; /* 往链表头插入数据 */
- pthread_mutex_unlock(&mutex); /* 解锁 */
- printf("produce %d\n",p_node->n_number);
- pthread_cond_signal(&cond); /* 发信号唤醒一个线程 */
- sleep(2); /* 休眠2秒 */
- }
- else
- {
- printf("product malloc node failed!\n");
- break;
- }
- }
- }
- int rt_application_init()
- {
- int result;
- /* 创建生产者线程,属性为默认值,入口函数是product,入口函数参数为NULL*/
- result = pthread_create(&tid1,NULL,product,NULL);
- check_result("product thread created ",result);
- /* 创建消费者线程,属性为默认值,入口函数是consumer,入口函数参数是NULL */
- result = pthread_create(&tid2,NULL,consumer,NULL);
- check_result("consumer thread created ",result);
- return 0;
- }