同步:所有线程以某种顺序依次访问临界资源。
同步机制的实现:条件变量—–是利用线程间共享的全局变量进行同步的机制。(一个线程等待“条件变量的条件成立”而挂起;另一个线程使“条件成立”(给出条件成立信号)。为了防止竞争资源,条件变量总是和互斥锁结合使用)
为什么要实现同步机制呢?(为了防止竞争资源)
以生产者消费者模型为例,首先我们需要了解什么是生产者消费者模型:(三种关系、两种角色、一个交易场所)
生产者—–生产者(互斥关系)
生产者—–消费者(同步与互斥关系)
消费者—–消费者(互斥关系)
交易场所—–链表
看下面的代码:
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include<pthread.h>
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; //mutex_lock
typedef struct _Node
{
int _val;
struct _Node *_next;
}Node,*pNode,**ppNode;
pNode _head= NULL;
pNode BuyNode(int val)
{
pNode tmp = (pNode)malloc(sizeof(Node));
if(tmp)
{
tmp->_val = val;
tmp->_next = NULL;
return tmp;
}
return NULL;
}
void Init_List(ppNode head)
{
*head = BuyNode(0);
}
//void Init_List()
//{
// _head = BuyNode(0);
//}
void Push(pNode head,int val)
{
pNode tmp = BuyNode(val);
tmp->_next = head->_next;
head->_next = tmp;
}
int Pop(pNode head)
{
if(!Is_Empty(head))
{
pNode tmp = head->_next;
int val=tmp->_val;
head->_next = tmp->_next;
free(tmp);
tmp = NULL;
return val;
}
return -1;
}
int Is_Empty(pNode head)
{
if(head->_next == NULL)
{
return 1;
}
return 0;
}
void Print_List(pNode head)
{
pNode tmp = head->_next;
while(NULL != tmp)
{
printf("%d ",tmp->_val);
tmp = tmp->_next;
}
printf("\n");
}
void* consumer(void* arg)
{
while(1)
{
pthread_mutex_lock(&lock);
int val=Pop(_head);
printf("consumer: %d\n",val);
pthread_mutex_unlock(&lock);
}
return (void*)0;
}
void* producer(void* arg)
{
while(1)
{
sleep(1);
pthread_mutex_lock(&lock);
int val = rand()%10000;
Push(_head,val);
printf("producer: %d\n",val);
pthread_mutex_unlock(&lock);
}
return (void*)0;
}
int main()
{
Init_List(&_head);//交易场所:链表
//Init_List();
pthread_t tid1,tid2;
pthread_create(&tid1,NULL,consumer,NULL);
pthread_create(&tid2,NULL,producer,NULL);
pthread_join(tid1,NULL);
pthread_join(tid2,NULL);
pthread_mutex_destroy(&lock);
//Push(_head,1);
//Push(_head,2);
//Push(_head,3);
//Push(_head,4);
//Push(_head,5);
//int val=Pop(_head);
//printf("%d\n",val);
//Print_List(_head);
return 0;
}
在上面的代码中生产者的优先级低于消费者,导致消费者一直在获得锁读取链表的数据再释放锁,紧接着又获得了锁,使得生产者一直处于挂起状态,而我们想要的是生产者生产一个数据,消费者消费一个数据。此时就需要引入同步机制(条件变量)使得消费者和生产者依次来访问该链表从而达到生产—消费平衡。
条件变量的创建:
静态创建:pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
动态创建:int pthread_cond_init(pthread_cond_t *restrict cond,const pthread_condattr_t *restrict attr);
参数1:条件变量cond的地址
参数2:条件变量cond的属性,设置为NULL
等待:
int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex);//在条件变量cond上阻塞式等待,这个函数做以下三步操作:
1. 释放Mutex
2. 阻塞等待
3. 当再次被唤醒时,自动获得Mutex
int pthread_cond_timedwait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex, const struct timespec *restrict abstime);//条件变量cond上非阻塞式等待,参数3可以设定等待超时,如果到达了abstime所指定的时刻仍然没有别的线程来唤醒当前线程,就返回ETIMEDOUT。
唤醒:
int pthread_cond_signal(pthread_cond_t *cond)//唤醒在该cond上等待的另⼀个线程
int pthread_cond_broadcast(pthread_cond_t *cond)//唤醒在该cond上等待的所有线程
条件变量的销毁:
int pthread_cond_destroy(pthread_cond_t *cond);
注:没有线程在该条件变量上等待时才可销毁该条件变量。
看下面的代码:
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include<pthread.h>
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; //mutex_lock
pthread_cond_t cond = PTHREAD_COND_INITIALIZER; //condition variable
typedef struct _Node
{
int _val;
struct _Node *_next;
}Node,*pNode,**ppNode;
pNode _head= NULL;
pNode BuyNode(int val)
{
pNode tmp = (pNode)malloc(sizeof(Node));
if(tmp)
{
tmp->_val = val;
tmp->_next = NULL;
return tmp;
}
return NULL;
}
void Init_List(ppNode head)
{
*head = BuyNode(0);
}
//void Init_List()
//{
// _head = BuyNode(0);
//}
void Push(pNode head,int val)
{
pNode tmp = BuyNode(val);
tmp->_next = head->_next;
head->_next = tmp;
}
int Pop(pNode head)
{
if(!Is_Empty(head))
{
pNode tmp = head->_next;
int val=tmp->_val;
head->_next = tmp->_next;
free(tmp);
tmp = NULL;
return val;
}
return -1;
}
int Is_Empty(pNode head)
{
if(head->_next == NULL)
{
return 1;
}
return 0;
}
void Print_List(pNode head)
{
pNode tmp = head->_next;
while(NULL != tmp)
{
printf("%d ",tmp->_val);
tmp = tmp->_next;
}
printf("\n");
}
void* consumer(void* arg)
{
while(1)
{
sleep(1);
pthread_mutex_lock(&lock);
while(Is_Empty(_head))
{
pthread_cond_wait(&cond,&lock);//生产者等待信号
}
int val=Pop(_head);
printf("consumer: %d\n",val);
pthread_mutex_unlock(&lock);
}
return (void*)0;
}
void* producer(void* arg)
{
while(1)
{
sleep(1);
pthread_mutex_lock(&lock);
int val = rand()%10000;
Push(_head,val);
printf("producer: %d\n",val);
pthread_mutex_unlock(&lock);
pthread_cond_signal(&cond);//生产者生产一个数据并发出信号
}
return (void*)0;
}
int main()
{
Init_List(&_head);//交易场所:链表
//Init_List();
pthread_t tid1,tid2;
pthread_create(&tid1,NULL,consumer,NULL);
pthread_create(&tid2,NULL,producer,NULL);
pthread_join(tid1,NULL);
pthread_join(tid2,NULL);
pthread_mutex_destroy(&lock);
pthread_cond_destroy(&cond);//条件变量的销毁
//Push(_head,1);
//Push(_head,2);
//Push(_head,3);
//Push(_head,4);
//Push(_head,5);
//int val=Pop(_head);
//printf("%d\n",val);
//Print_List(_head);
return 0;
}