目录
1. 条件变量的原理
条件变量不是锁,但是条件变量可以阻塞线程,一般将条件变量和互斥锁共同使用,其中互斥锁是为了保护一块区域,条件变量则是用于阻塞线程。一般条件变量的工作方式可以概括为2步
(1)条件不满足,阻塞线程。
(2)条件满足时,通知线程开始工作
2.条件变量常用的函数
1.定义条件变量
pthread_cond_t cond;
2.初始化条件变量
pthread_cond_init(
pthread_cond_t *restrict cond;
const pthread_condattr_t *restrict attr;
);
示例:
pthread_cond_init(&cond, NULL); 动态初始化。
pthread_cond_t cond = PTHREAD_COND_INITIALIZER; 静态初始化。
3. 销毁条件变量
pthread_cond_destory(pthread_cond_t *cond);
4.阻塞一个条件变量
pthread_cond_wait(
pthread_cond_t *restrict cond,
pthread_mutex_t *restrict mutex
);
介绍:
1) 阻塞等待条件变量满足
2) 解锁已经加锁成功的信号量 (相当于 pthread_mutex_unlock(&mutex)),12两步为一个原子操作
3) 当条件满足,函数返回时,解除阻塞并重新申请获取互斥锁。重新加锁信号量 (相当于,
pthread_mutex_lock(&mutex))
5.限时等待一个条件变量
pthread_cond_timewait(
pthread_cond_t *restrict cond,
pthread_mutex_t *restrict mutex,
const struct timespec *restrict abstime
);
6.唤醒阻塞线程
唤醒一个阻塞线程
pthread_cond_signal(ptread_cond_t *cond);
唤醒全部阻塞线程
pthread_cond_broadcast(pthread_cond_t *cond);
3.生产者和消费者模型
本节将通过生成者和消费者模型来总结条件变量的使用过, 模型分析:
生产者:生产数据,加锁数据,将数据写入公共区域,解锁,通知阻塞在条件变量上线程,最后继续生产数据
消费者:创建锁,初始化,加锁,等待条件满足,访问共享数据,解锁,释放条件变量和锁。
4.示例:
代码实现链表的头插法插入
头插法过程分析
代码分析
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <pthread.h>
void err_thread(int ret, char *str)
{
if (ret != 0) {
fprintf(stderr, "%s:%s\n", str, strerror(ret));
pthread_exit(NULL);
}
}
/*链表作为公享数据,需被互斥量保护*/
struct msg {
struct msg *next;
int num;
};
/*头结点*/
struct msg *head;
/*静态初始化,条件变量和一个互斥锁*/
pthread_cond_t has_product = PTHREAD_COND_INITIALIZER;
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
/*消费者*/
void *consumer(void *p)
{
/*消费者循环消费, 创建和初始化已经在开始,静态定义*/
while(1)
{
struct msg *mp;
/*3. 上锁*/
pthread_mutex_lock(&lock)
/*头结点为空,没有结点解锁,解锁并阻塞等待*/
/*4. 条件满足,处理数据,条件不满足阻塞数据*/
while(head == NULL)
{
/*解锁并阻塞等待*/
pthread_cond_wait(&has_product, &lock);
}
/*5. 访问共享数据*/
mp = head;
head = mp->next; //模拟消费掉一个产品
/*6. 解锁,释放条件变量,释放锁*/
pthread_mutex_unlock(&lock);
printf("-Consume %lu---%d\n", pthread_self(), mp->num);
free(mp);
sleep(rand() % 5);
}
return NULL;
}
/*生产者*/
void *producer(void *arg)
{
/*定义结构体变量*/
while(1)
{
struct msg *mp = malloc(sizeof(struct msg));
/*1.生产数据*/
mp -> num = rand() %1000 + 1;
printf("-Produce %d\n", mp->num);
/*头插法实现链表插入:生产的即为插入的结点*/
/*2.加锁*/
pthread_mutex_lock(&lock);
/*3. 将数据发到公共区域,头插法加入链表*/
mp->next = head;
head = mp;
/*4. 解锁*/
pthread_mutex_unlock(&lock);
/*5. 通知唤醒阻塞在条件变量上的线程*/
pthread_cond_signal(&has_product);
sleep(rand() % 5);
}
return NULL;
}
int main(int argc, char *argv[])
{
int ret;
pthread_t pid, cid;
srand(time(NULL));
/*创建两个线程分别作为生产者和消费者*/
ret = pthread_create(&pid, NULL, producer, NULL);
if (ret != 0)
err_thread(ret, "pthread_create produser error");
ret = pthread_create(&cid, NULL, consumer, NULL);
if(ret != 0)
err_thread(ret, "pthread_create produser error");
/*释放两个线程*/
pthread_join(pid, NULL);
pthread_join(cid, NULL);
return 0;
}
结果: