【Linux】条件变量

什么是条件变量:

与互斥锁不同,条件变量是用来等待而不是用来上锁的,一个条件变量对应一个等待队列。条件变量是利用线程间共享的全局变量进行同步的一种机制,主要包括两个动作:一个线程等待"条件变量的条件成立"而挂起;另一个线程使"条件成立"(给出条件成立信号)唤醒线程。

条件变量的使用:

1.条件变量初始化:

1)pthread_cond_t cond=PTHREAD_COND_INITIALIZER;2int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t   *cond_attr);

参数:cond为条件变量的地址, cond_attr用来设置条件变量的属性,尽管POSIX标准中为条件变量定义了属性,但在LinuxThreads中没有实现,因此cond_attr值通常为NULL,且被忽略。
返回值:成功返回0,失败返回错误码。

2.销毁条件变量

int pthread_cond_destroy(pthread_cond_t  *cond) ; 

只有在没有线程在该条件变量上等待的时候才能注销这个条件变量,否则返回EBUSY。因为Linux实现的条件变量没有分配什么资源,所以注销动作只包括检查是否有等待线程。

3.等待

int pthread_cond_wait(pthread_cond_t *cond,pthread_mutex_t *mutex);  

(1)条件变量必须和互斥锁一起使用, 互斥锁是用来保护“条件”的,“条件”肯定是一个共享资源,通过对“条件”状态的判断,决定是否将线程放在阻塞队列中。(例如:变量a可以是一个“条件”,规定当a等于零时,线程入阻塞队列)。但是从判断某“条件”成立,到线程入阻塞队列不是原子的,入阻塞队列后线程肯定期待别的线程通过对条件的改变,将他唤醒。这样可能出现的问题是,还没入阻塞队列,但是别的线程已经通过条件的改变,发出了唤醒信号。唤醒信号没有作用,因为阻塞队列没有线程。等时间片切换回来,再将线程入队列,就不会再被唤醒了。所以要用锁保护“条件”,在线程未真正放入阻塞队列之前不释放锁,保证条件不会被别的资源改变。当然在该线程判断条件之前要线上锁。
(2)条件变量的工作流程:

mutex.lock();
while (判断“条件”是否成立) {
    pthread_cond_wait 等待
}
修改”条件“
mutext.unlock();
pthread_cond_signal 唤醒

pthread_cond_wait(cond, mutex)要完成的功能是,(1)将线程加入cond对应的等待队列。(2) 释放mutex锁。(3)当线程被唤醒后还在pthread_cond_wait()体内,然后继续加mutex加锁,获得锁资源。需要注意的是(1)和 (2)是原子操作,所以不用在意他们的先后顺序。当完成pthread_cond_wait操作后拿到锁资源后为什么还要判断“条件”,因为可能有多个线程竞争锁资源一个线程拿到锁资源后对“条件”改变,其他线程拿到做资源应该再次判断是否等待。所以需要循环判断。

唤醒

int pthread_cond_signal(pthread_cond_t *cptr); 
int pthread_cond_broadcast (pthread_cond_t * cptr);
//成功返回0,失败返回错误码.

唤醒条件有两种形式,pthread_cond_signal()唤醒一个等待该条件的线程,存在多个等待线程时按入队顺序唤醒其中一个;而pthread_cond_broadcast()则唤醒所有等待线程。

生产者消费者模型实现

生产者消费者模型是指,有一块缓冲区,缓冲区有大小为 capacity。有多个生产者向缓冲区里边放数据,多个消费者从缓冲区里边取数据。
分析:(1)缓冲区满,不能放数据。(2)缓冲区空不能取数据 (3)缓冲区同时只能有一个消费者取,或者一个生产者放。
实现如下:

#include<unistd.h>
#include<iostream>
#include<queue>
#include<pthread.h>
using namespace std;
#define MAX_QUEUE 5
static int i = 0;
class BlockQueue{
public:	
	BlockQueue(int qmax = MAX_QUEUE)//构造函数
		:_capacity(qmax)
	{
		pthread_mutex_init(&_mutex,NULL);
		pthread_cond_init(&_cond_product, NULL);
		pthread_cond_init(&_cond_consumer, NULL);
	}
	~BlockQueue(){//析构函数
		pthread_mutex_destroy(&_mutex);
		pthread_cond_destroy(&_cond_product);
		pthread_cond_destroy(&_cond_consumer);
	}
	bool push(int date){
     pthread_mutex_lock(&_mutex);
     while((int)_queue.size() == _capacity){
        pthread_cond_wait(&_cond_product, &_mutex);//解锁等待,唤醒后加锁
     }
     
     _queue.push(date);
     pthread_mutex_unlock(&_mutex);
     pthread_cond_signal(&_cond_consumer);
     return true;
  }
  bool pop(int* data){
      while(_queue.size() == 0){//拿到锁资源后,判断是否满足等待条件
			  pthread_cond_wait(&_cond_consumer, &_mutex);//解锁等待,唤醒后加锁
      }
		  //不满足等待条件
		  *data = _queue.front();//出队列
		  //sleep(1);
      _queue.pop();
		  pthread_mutex_unlock(&_mutex);//操作完临界资源后解锁
		  pthread_cond_signal(&_cond_product);//唤醒生产者线程
		  return true;
	}
private:
	pthread_cond_t _cond_product;
	pthread_cond_t _cond_consumer;
	pthread_mutex_t _mutex;
	queue<int> _queue;
	int _capacity;
};
void* thr_product(void* grm){
	BlockQueue *ptr = (BlockQueue*)grm;
	while(1){
		//生产者不断地写入数据
		ptr ->push(i++);
		printf("写入数据%d\n",i);//注意printf()和上边的push不是原子操作
	}
	return NULL;
}
void* thr_consumer(void* grm){
	BlockQueue *ptr = (BlockQueue*)grm;
	int date;
	while(1){
		//消费者不断地读出数据
		ptr ->pop(&date);
		printf("读出数据%d\n",date);
	}
	return NULL;
}
int main(){
	BlockQueue q(3);//阻塞队列的容量为3
	pthread_t pro_tid[4];//4个生产者
	pthread_t con_tid[4];//4个消费者
	for(int i = 0; i < 4; i++){
		int res	= pthread_create(&pro_tid[i],NULL, thr_product, (void*)&q);
		if(res != 0){
			printf("创建生产者线程失败");
			return -1;
		}
	}
	for(int i = 0; i < 4; i++){
		int res	= pthread_create(&con_tid[i],NULL, thr_consumer, (void*)&q);
		if(res != 0){
			printf("创建消费者线程失败");
			return -1;
		}
	}
	pthread_join(pro_tid[0],NULL);
	pthread_join(con_tid[0],NULL);
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值