一.条件变量
条件变量是用来等待线程而不是上锁的,条件变量通常和互斥锁一起使用。条件变量之所以要和互斥锁一起使用,主要是因为互斥锁的一个明显的特点就是它只有两种状态:锁定和非锁定,而条件变量可以通过允许线程阻塞和等待另一个线程发送信号来弥补互斥锁的不足,所以互斥锁和条件变量通常一起使用。
当条件满足的时候,线程通常解锁并等待该条件发生变化,一旦另一个线程修改了环境变量,就会通知相应的环境变量唤醒一个或者多个被这个条件变量阻塞的线程。这些被唤醒的线程将重新上锁,并测试条件是否满足。一般来说条件变量被用于线程间的同步;当条件不满足的时候,允许其中的一个执行流挂起和等待。
条件变量中常用的API:
1).条件变量类型为:pthread_cond_t ,类似互斥变量,条件变量的初始化有两种方式:
静态:pthread_cond_t mycon=PTHREAD_COND_INITIALIZER;
动态:通过调用pthread_cond_init函数,函数原型为:
静态:pthread_cond_t mycon=PTHREAD_COND_INITIALIZER;
cond:环境变量.
attr:条件变量属性.
成功返回0,失败返回错误码.
2).条件变量摧毁函数:pthread_cond_destroy(&mycond);
int pthread_cond_destroy(pthread_cond_t *cond);
成功返回0,失败返回错误码.
摧毁所指定的条件变量,同时将会释放所给它分配的资源。调用该函数的进程也并不等待在参数所指定的条件变量上。
3).条件变量等待函数。pthread_cond_wait(&mycond,&mylock);
int pthread_cond_timedwait(pthread_cond_t *restrict cond,
pthread_mutex_t *restrict mutex,
const struct timespec *restrict abstime);
int pthread_cond_wait(pthread_cond_t *restrict cond,
pthread_mutex_t *restrict mutex);
cond:条件变量
mutex:互斥锁
pthread_cond_wait和pthread_cond_timedwait的区别:
pthread_cond_timedwait函数类型与函数pthread_cond_wait,区别在于,如果达到或是超过所引用的参数*abstime,它将结束并返回错误ETIME.
timespec
typedef struct timespec
{
time_t tv_sec; //!> 秒
long tv_nsex; //!> 毫秒
}timespec_t;
当时间超过之前预设定的时会返回错误.
4).条件变量通知函数:pthread_cond_signal和pthread_cond_broadcast
int pthread_cond_broadcast(pthread_cond_t *cond);
int pthread_cond_signal(pthread_cond_t *cond);
pthread_cond_signal和pthread_cond_broadcast的区别:
pthread_cond_signal:只唤醒一个在相同条件变量中阻塞的线程将会被唤醒
pthread_cond_broadcast:唤醒等待队列中的所有线程
二.一个关于互斥锁和条件变量的栗子
栗子:用互斥锁和条件变量的概念实现一个简单的生产者和消费者的模型。
生产者和消费者模型:
1).满足互斥与同步条件,用互斥锁和条件变量实现
2).多个生产者和消费者:生产者和生产者属于互斥关系;生产者和消费者属于互斥和同步关系;消费者和消费者属于竞争关系,需要互斥锁
3).生产者和消费者模型中存在如下几种关系和角色:3种关系,2种角色,1种交换媒介(一般是一段内存)
下例以单生产者和单消费者,交换媒介为链表实现的生产者消费者模型
procon.c
#include
#include
#include
#include
typedef struct LinkNode
{
int data;
struct LinkNode *next;
}Node;
pthread_mutex_t mylock=PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t mycond=PTHREAD_COND_INITIALIZER;
Node *CreatNode(int data)
{
Node *NewNode=(Node *)malloc(sizeof(Node));
if(NULL == NewNode)
{
perror("malloc");
return NULL;
}
NewNode->data=data;
NewNode->next=NULL;
return NewNode;
}
void InitLink(Node **head)
{
*head=CreatNode(0);
}
int IsEmpty(Node *head)
{
assert(head);
if(head->next)
return 0; //not empty
else
return 1; //empty
}
void PushFront(Node *head,int data)
{
assert(head);
Node *NewNode=CreatNode(data);
NewNode->next=head->next;
head->next=NewNode;
}
void PopFront(Node *head,int *data)
{
assert(data);
assert(head);
if(IsEmpty(head))
{
printf("empty link\n");
return ;
}
Node *del=head->next;
*data=del->data;
head->next=del->next;
free(del);
del=NULL;
}
void DisplayLink(Node *head)
{
assert(head);
Node *cur=head->next;
while(cur)
{
printf("%d ",cur->data);
cur=cur->next;
}
printf("\n");
}
void DestroyLink(Node *head)
{
int data=0;
assert(head);
while(!IsEmpty(head))
{
PopFront(head,&data);
}
free(head);
}
void *product_run(void *arg)
{
int data=0;
Node *head=(Node *)arg;
while(1)
{
usleep(100000);
data=rand()%1000;
pthread_mutex_lock(&mylock);
PushFront(head,data);
pthread_mutex_unlock(&mylock);
pthread_cond_signal(&mycond);
printf("product is done,data=%d\n",data);
}
}
void *consumer_run(void *arg)
{
int data=0;
Node *head=(Node *)arg;
while(1)
{
pthread_mutex_lock(&mylock);
while(IsEmpty(head))
{
pthread_cond_wait(&mycond,&mylock);
}
PopFront(head,&data);
pthread_mutex_unlock(&mylock);
printf("consumer is done,data=%d\n",data);
}
}
void testprocon()
{
Node *head=NULL;
InitLink(&head);
pthread_t tid1;
pthread_t tid2;
pthread_create(&tid1,NULL,product_run,(void *)head);
pthread_create(&tid2,NULL,consumer_run,(void *)head);
pthread_join(tid1,NULL);
pthread_join(tid2,NULL);
DestroyLink(head);
pthread_mutex_destroy(&mylock);
pthread_cond_destroy(&mycond);
}
int main()
{
testprocon();
return 0;
}
Makefile
procon:procon.c
gcc -o $@ $^ -lpthread
.PHONY:clean
clean:
rm -f procon
总结:
条件变量用在某个线程需要在某种条件才去保护它将要操作的临界区的情况下,从而避免了线程不断轮询检查该条件是否成立而降低效率的情况,这是实现了效率提高。
希望对读者有帮助吧~~~~