参考:https://blog.csdn.net/qq_37653144/article/details/81988615
https://blog.csdn.net/qq_34328833/article/details/56012780
https://blog.csdn.net/lycommand/article/details/79652403
互斥锁只有两种状态,他的用途相对来说比较有限。除了互斥锁之外,还可以用条件变量解决线程同步的问题,条件变量是对互斥锁的补充,它允许线程阻塞并且等待另一个线程发送的信号,当收到信号之后,阻塞的线程就被唤醒并锁定与之相关的互斥锁。
条件变量是用来等待线程而不是上锁的,条件变量通常和互斥锁一起使用。条件变量之所以要和互斥锁一起使用,主要是因为互斥锁的一个明显的特点就是它只有两种状态:锁定和非锁定,而条件变量可以通过允许线程阻塞和等待另一个线程发送信号来弥补互斥锁的不足,所以互斥锁和条件变量通常一起使用。
当条件满足的时候,线程通常解锁并等待该条件发生变化,一旦另一个线程修改了环境变量,就会通知相应的环境变量唤醒一个或者多个被这个条件变量阻塞的线程。这些被唤醒的线程将重新上锁,并测试条件是否满足。一般来说条件变量被用于线程间的同步;当条件不满足的时候,允许其中的一个执行流挂起和等待。
利用条件变量解决生产者消费者问题
//
// main.cpp
// 条件变量解决线程同步
//
// Created by 蓝猫 on 2018/11/26.
// Copyright © 2018年 蓝猫. All rights reserved.
//生产者消费者问题
#include <iostream>
#include <stdio.h>
#include <queue>
#include <pthread.h>
#include <memory>
#include <stdlib.h>
#include <unistd.h>
#define Buffer_Size 4
#define Over -1
template<typename T>
class Producer//定义生产者条件变量结构
{
public:
T buffer[Buffer_Size];//缓冲区
pthread_mutex_t lock;//定义缓冲区互斥锁
int readpos;//读取的位置 队头
int writepos;//写入位置 队尾
std::queue<T> q;
pthread_cond_t notempty;//缓冲区又数据的标记
pthread_cond_t notfull;//缓冲区没有数据的时候标记
Producer();
};
template<typename T>
Producer<T>::Producer()
{
pthread_mutex_init(&lock, NULL);
pthread_cond_init(¬full, NULL);
pthread_cond_init(¬empty, NULL);
readpos=0;
writepos=0;
}
Producer<int> b;
//std::shared_ptr<Producer> buffer=std::make_shared<Producer>();
//在缓冲区中存放数据
void put(Producer<int> &b,int data)
{
pthread_mutex_lock(&b.lock);// 加锁
//如果队列满了 数据为空的信号要等待 队满的条件是 队尾加1就是队头
//printf("队尾%d\n",b.writepos);
while ((b.writepos+1)%Buffer_Size==b.readpos)
{
pthread_cond_wait(&b.notfull, &b.lock);
}
b.buffer[b.writepos]=data;
b.writepos++;
printf("生产者生产了%d\n",data);
if(b.writepos>=Buffer_Size)
{
b.writepos=0;
}
pthread_cond_signal(&b.notempty);//发送现在数据不满的信号
pthread_mutex_unlock(&b.lock);//解锁
}
int get(Producer<int> &b)
{
int data;
pthread_mutex_lock(&b.lock);// 加锁
//如果队列为空 数据不空的信号要等待 队满的条件是
//printf("队头%d\n",b.readpos);
while (b.writepos==b.readpos)
{
pthread_cond_wait(&b.notempty, &b.lock);
}
data=b.buffer[b.readpos];
printf("消费者消费了了%d\n",data);
b.readpos++;
if(b.readpos>=Buffer_Size)
{
//!!!!错在这里
//b.writepos=0;
b.readpos=0;
}
pthread_cond_signal(&b.notfull);//发送现在数据不满的信号
pthread_mutex_unlock(&b.lock);//解锁
return data;
}
void *producer(void *arg)
{
for(int i=0;i<9;i++)
{
//std::cout<<"生产者:"<<i<<std::endl;
//printf("生产者:%d\n",i);
put(b, i);
}
put(b, Over);
pthread_exit(NULL);
}
void *consumer(void *arg)
{
sleep(1);
int cons;
while (cons!=Over)
{
cons=get(b);
// std::cout<<"消费者:"<<cons<<std::endl;
//printf("消费者:%d\n",cons);
}
pthread_exit(NULL);
}
int main(int argc, const char * argv[])
{
pthread_t pro,cons;
pthread_create(&pro, NULL, producer, NULL);
pthread_create(&cons, NULL, consumer, NULL);
pthread_join(pro, NULL);
pthread_join(cons, NULL);
return 0;
}
两个线程,生产者和消费者线程分别从缓冲区生产和消费数据,缓冲区存储设置为4,因为是循环队列,缓冲区要浪费一个存储节点来判断队列满,如果队列满,阻塞生产者线程,如果空,阻塞消费者线程。下图,生产者生产0,1,2之后阻塞,但线程随机运行,没有体现队列空的情况,但并不碍事。