目录
1、条件变量
linux多线程的环境中使用互斥锁mutex来保护变量的同步,但互斥锁只有两种状态锁定、非锁定,如果某个变量的条件没有满足就对变量进行加锁处理就会白白的损耗CPU,这时可以使用条件变量pthread_cond_t,条件变量pthread_cond_t一般是和互斥锁配合使用,当条件不满足条件变量就会挂起并释放互斥锁,这时就不会损耗CPU,直到条件满足条件变量就会重新加互斥锁保护。
2、条件变量API
2.1 pthread_cond_init
条件变量的初始化有动态初始化、静态初始化两种方式,调用pthread_cond_init就是动态初始化条件变量,catter是条件变量的属性,如果是缺省属性就是NULL,我们一般是设置为缺省属性。
#include <pthread.h>
int pthread_cond_init(pthread_cond_t *cv,
const pthread_condattr_t *cattr);
返回值:函数成功返回0;任何其他返回值都表示错误
静态初始化是调用宏PTHREAD_COND_INITIALIZER,默认是缺省模式。
pthread_cond_t cv = PTHREAD_COND_INITIALIZER;
2.2 pthread_cond_wait
pthread_cond_wait函数会释放互斥锁mutex,然后线程阻塞挂起,直到被pthread_cond_singal函数、pthread_cond_broadcast唤醒,或者被信号中断后唤醒。
pthread_cond_wait调用格式如下:
pthread_mutex_lock();
while(condition_is_false)
pthread_cond_wait();
pthread_mutex_unlock;
这里用while而不是用if判断条件是否满足,因为条件阻塞时可能被一个信号中断唤醒,但这时条件还没有准备好,所以要再次调用pthread_cond_wait()挂起线程。
调用pthread_cond_wait处理过程:
释放互斥锁 --> 进程阻塞挂起
pthread_cond_wait收到唤醒信号处理过程:
收到唤醒信号-->对互斥锁加锁
pthread_cond_wait函数:
#include <pthread.h>
int pthread_cond_wait(pthread_cond_t *cv, pthread_mutex_t *mutex);
返回值:函数成功返回0;任何其他返回值都表示错误
2.3 pthread_cond_timedwait
pthread_cond_timedwait在pthread_cond_wait的基础上添加一个超时时间,如果超时pthread_cond_timedwait就会返回这时会对互斥锁重新加锁。
#include <pthread.h>
#include <time.h>
int pthread_cond_timedwait(pthread_cond_t *cv,
pthread_mutex_t *mp, const structtimespec * abstime);
返回值:函数成功返回0;任何其他返回值都表示错误
abstime使用:
pthread_timestruc_t to;
to.tv_sec = time(NULL) + TIMEOUT; //秒
to.tv_nsec = 0; //毫秒
2.4 pthread_cond_signal
pthread_cond_signal用来释放阻塞在条件变量上的线程,如果有对个线程被条件变量阻塞,pthread_cond_signal只释放一个线程,至于释放那个由系统调度策略决定。条件变量必须在互斥锁 的保护下使用,否则释放条件变量可能在锁定条件变量之前调用那么就会进入死锁状态。
pthread_cond_signal:
#include <pthread.h>
int pthread_cond_signal(pthread_cond_t *cv);
返回值:函数成功返回0;任何其他返回值都表示错误
2.5 pthread_cond_broadcast
pthread_cond_broadcast会唤醒所有阻塞条件变量cv上的线程。当所有线程被唤醒就会进入加互斥锁的竞争中。
#include <pthread.h>
int pthread_cond_broadcast(pthread_cond_t *cv);
返回值:函数成功返回0;任何其他返回值都表示错误
2.6 pthread_cond_destroy
pthread_cond_destroy会销毁条件变量cv,释放条件变量使用的空间。
#include <pthread.h>
int pthread_cond_destroy(pthread_cond_t *cv);
返回值:函数成功返回0;任何其他返回值都表示错误
3、唤醒丢失问题
在线程未获得相应的互斥锁时调用pthread_cond_signal或pthread_cond_broadcast函数可能会引起唤醒丢失问题。
唤醒丢失往往会在下面的情况下发生:
- 一个线程调用pthread_cond_signal或pthread_cond_broadcast函数;
- 另一个线程正处在测试条件变量和调用pthread_cond_wait函数之间;
- 没有线程正在处在阻塞等待的状态下。
4、生产者、消费者例子
(1)不使用条件变量
不使用条件变量:
#include<stdio.h>
#include<unistd.h>
#include<pthread.h>
#include<stdlib.h>
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
typedef struct node{
int data;
struct node * next;
}*node_p;
node_p head = NULL;
node_p init_node(int data){
node_p tmp;
tmp = (node_p) malloc(sizeof(struct node));
if(tmp == NULL){
printf("memeroy out!\n");
return NULL;
}
tmp->data = data;
return tmp;
}
void push_node(node_p head, int data){
node_p tmp = init_node(data);
if(tmp == NULL){
printf("push_node err!\n");
return;
}
tmp->next = head->next;
head->next = tmp;
}
int pop_node(node_p head){
int ret = -1;
if(head->next){
node_p tmp;
tmp = head->next;
head->next = tmp->next;
ret = tmp->data;
free(tmp);
}
return ret;
}
void show_node(node_p head){
node_p tmp;
tmp = head->next;
while(tmp){
printf("data:%d\n", tmp->data);
tmp = tmp->next;
}
}
void *consumer(void *argv){
while(1){
sleep(1);
pthread_mutex_lock(&lock);
if(head->next == NULL){
printf("producter not read...\n");
}else{
printf("consumer:%d\n", pop_node(head));
}
pthread_mutex_unlock(&lock);
}
}
void *producter(void *argv){
int data;
while(1){
sleep(5);
pthread_mutex_lock(&lock);
data = rand()%2019;
push_node(head, data);
printf("producter:%d\n", data);
pthread_mutex_unlock(&lock);
}
}
int main(){
pthread_t t1, t2;
head = init_node(0);
if(head == NULL){
printf("init head fail...\n");
return -1;
}
pthread_create(&t1, NULL, consumer, NULL);
pthread_create(&t2, NULL, producter, NULL);
pthread_join(t1, NULL);
pthread_join(t2, NULL);
}
运行结构如下:
root@ubuntu:/home/c_test/cond# gcc producter_consumer.c -lpthread
root@ubuntu:/home/c_test/cond# ./a.out
producter not read...
producter not read...
producter not read...
producter not read...
producter:1957
consumer:1957
producter not read...
producter not read...
producter not read...
producter not read...
producter:766
consumer:766
producter not read...
producter not read...
producter not read...
producter not read...
producter:1050
consumer:1050
我们可以看到当生产者没有准备好时,消费者就会一致占用CPU,这样是在浪费CPU时间。
(2)加条件变量
#include<stdio.h>
#include<unistd.h>
#include<pthread.h>
#include<stdlib.h>
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
typedef struct node{
int data;
struct node * next;
}*node_p;
node_p head = NULL;
node_p init_node(int data){
node_p tmp;
tmp = (node_p) malloc(sizeof(struct node));
if(tmp == NULL){
printf("memeroy out!\n");
return NULL;
}
tmp->data = data;
return tmp;
}
void push_node(node_p head, int data){
node_p tmp = init_node(data);
if(tmp == NULL){
printf("push_node err!\n");
return;
}
tmp->next = head->next;
head->next = tmp;
}
int pop_node(node_p head){
int ret = -1;
if(head->next){
node_p tmp;
tmp = head->next;
head->next = tmp->next;
ret = tmp->data;
free(tmp);
}
return ret;
}
void show_node(node_p head){
node_p tmp;
tmp = head->next;
while(tmp){
printf("data:%d\n", tmp->data);
tmp = tmp->next;
}
}
void *consumer(void *argv){
while(1){
sleep(1);
pthread_mutex_lock(&lock);
while(head->next == NULL){
printf("producter not read...\n");
pthread_cond_wait(&cond, &lock);
break;
}
printf("consumer:%d\n", pop_node(head));
pthread_mutex_unlock(&lock);
}
}
void *producter(void *argv){
int data;
while(1){
sleep(5);
pthread_mutex_lock(&lock);
data = rand()%2019;
push_node(head, data);
printf("producter:%d\n", data);
pthread_cond_signal(&cond);
pthread_mutex_unlock(&lock);
}
}
int main(){
pthread_t t1, t2;
head = init_node(0);
if(head == NULL){
printf("init head fail...\n");
return -1;
}
pthread_create(&t1, NULL, consumer, NULL);
pthread_create(&t2, NULL, producter, NULL);
pthread_join(t1, NULL);
pthread_join(t2, NULL);
pthread_cond_destroy(&cond);
}
运行结果:
root@ubuntu:/home/c_test/cond# gcc producter_consumer_cond.c -lpthread
root@ubuntu:/home/c_test/cond# ./a.out
producter not read...
producter:1957
consumer:1957
producter not read...
producter:766
consumer:766
producter not read...
producter:1050
consumer:1050
producter not read...
producter:1165
consumer:1165
加了条件变量后当条件没有准备好时wait_cond_wait就会释放互斥锁消费者线程挂起,消费者让出CPU给生产者而不会白白占用CPU。