信号量是一个计数器本质是对特定资源的预定机制。
多线程使用资源:
1.将目标资源整体使用 锁+二元信号量
2.将目标资源按照块分批使用 需要阻止过多资源同时进入,不要访问同一个位置
生产消费模型:
1.生产者和消费者是解耦的。2.支持忙闲不均。3.提高效率,不是体现在入交易场所和出交易场所上,而在于未来获取任务和处理具体任务是并发的。
现在有个环形队列用于描述生产者消费者模型,将环形队列看成长度为N的数组,指针++完之后模运算就回到原点模拟出环形队列。
生产者和消费者对应两个指针,在环形队列里,一个负责放苹果一个负责拿苹果,生产者从头开始放苹果一直到生产满了回到原点。就轮到消费者去消费拿苹果。
约定:
1.空生产者先运行
2.满消费者先运行
3.生产者不能多消费者一轮以上
4.消费者不能超过生产者
环形队列,不为空或不为满,生产和消费可以同时进行
环形队列,为空为满,生产和消费需要同步互斥
用信号量保证这四个约定。
生产者关注空位置,消费者关注有效数据
刚开始 sem_blank=N sem_data=0
模拟追逐过程:
生产者:P操作申请资源,对空格子--,空格子被占用,多了数据就要V操作去对生产者的指针++
消费者:P操作申请资源对指针++,数据--,V操作空各子++
信号量原子申请成功继续运行,申请失败申请的线程就被阻塞,数据信号量为0消费者P操作阻塞,生产者空格子多可以随时运行,所以可以继续P操作后接着运行直到N--到0信号量为0,再去P操作无法进行被阻塞,同时消费者那边的sem_data数据的信号量一直++P操作就会运行,然后就数据就去--直到为0,V操作空格子++生产者可以继续运行,这样就可以为空时生产者先运行,为满时消费者先运行且生产者不会多出消费者一轮。
cpp
#include <iostream>
#include <semaphore.h>
#include <pthread.h>
namespace SemModule
{
const int defaultvalue = 1;
class Sem
{
public:
Sem(unsigned int sem_value = defaultvalue)
{
sem_init(&_sem, 0, sem_value); // 创建信号量
}
void P()
{
int n = sem_wait(&_sem);
(void)n;
}
void V()
{
int n = sem_post(&_sem);
}
~Sem()
{
sem_destroy(&_sem); // 销毁信号量
}
private:
// int vaule;//信号量起始地址
sem_t _sem;
};
};
cpp
#pragma once
#include <iostream>
#include <vector>
#include "Sem.hpp"
static const int gcap = 5; // 容量 for debug
using namespace SemModule;
template <typename T>
class RingQueue
{
public:
RingQueue(int cap = gcap)
: _cap(cap),
_rq(cap),
_blank_sem(cap),
_p_step(0),
_data_sem(0),
_c_step(0)
{
}
void Equeue(const T &in)
{
// 生产者
// 1.申请信号量,空位置信号量
_blank_sem.P();
// 2.生产
_rq[_p_step] = in;
// 3.更新下标
++_p_step;
// 4.维持环形特性
_p_step %= _cap;
_data_sem.V();
}
void Pop(T *out)
{
// 消费者
// 1.申请信号量,空位置信号量
_data_sem.P();
// 2.消费
*out = _rq[_c_step];
// 3.更新下标
++_c_step;
// 4.维持环形特性
_c_step %= _cap;
_blank_sem.V();
}
~RingQueue()
{
}
private:
std ::vector<T> _rq;
int _cap; // 容量
// 生产者
Sem _blank_sem; // 空位置
int _p_step; // 生产下标位置
// 消费者
Sem _data_sem; // 数据
int _c_step; // 生产下标位置
};
cpp
#include "RingQueue.hpp"
#include <unistd.h>
#include <pthread.h>
void *consumer(void *args)
{
RingQueue<int> *rq = static_cast<RingQueue<int> *>(args);
while (true)
{
sleep(2);
// 1.消费任务
int t = 0;
rq->Pop(&t);
// 2.处理任务
std::cout << "消费者拿到了一个数据: " << t << std::endl;
}
}
void *productor(void *args)
{
RingQueue<int> *rq = static_cast<RingQueue<int> *>(args);
int data = 1;
while (true)
{
// sleep(2);
// 1.获得任务
std::cout << "生产了一个任务: " << data << std::endl;
// 2.生产任务
rq->Equeue(data);
data++;
}
}
int main()
{
RingQueue<int> *rq = new RingQueue<int>();
pthread_t c[1], p[1];
pthread_create(c, nullptr, consumer, rq);
pthread_create(p, nullptr, productor, rq);
pthread_join(c[0], nullptr);
pthread_join(p[0], nullptr);
return 0;
}
以上是单生产单消费,多生产多消费需考虑互斥关系,就要加锁。
cpp
#pragma once
#include <iostream>
#include <pthread.h>
namespace MutexModule
{
class Mutex
{
public:
Mutex()
{
pthread_mutex_init(&_mutex, nullptr);
}
void Lock()
{
int n = pthread_mutex_lock(&_mutex);
(void)n;
}
void Unlock()
{
int n = pthread_mutex_unlock(&_mutex);
(void)n;
}
~Mutex()
{
pthread_mutex_destroy(&_mutex);
}
pthread_mutex_t *Get()
{
return &_mutex;
}
private:
pthread_mutex_t _mutex;
};
class LockGuard
{
public:
LockGuard(Mutex &mutex):_mutex(mutex)
{
_mutex.Lock();
}
~LockGuard()
{
_mutex.Unlock();
}
private:
Mutex &_mutex;
};
};
cpp
#include "RingQueue.hpp"
#include <unistd.h>
#include <pthread.h>
void *consumer(void *args)
{
RingQueue<int> *rq = static_cast<RingQueue<int> *>(args);
while (true)
{
sleep(2);
// 1.消费任务
int t = 0;
rq->Pop(&t);
// 2.处理任务
std::cout << "消费者拿到了一个数据: " << t << std::endl;
}
}
void *productor(void *args)
{
RingQueue<int> *rq = static_cast<RingQueue<int> *>(args);
int data = 1;
while (true)
{
// sleep(2);
// 1.获得任务
std::cout << "生产了一个任务: " << data << std::endl;
// 2.生产任务
rq->Equeue(data);
data++;
}
}
int main()
{
RingQueue<int> *rq = new RingQueue<int>();
pthread_t c[1], p[1];
pthread_create(c, nullptr, consumer, rq);
pthread_create(p, nullptr, productor, rq);
pthread_join(c[0], nullptr);
pthread_join(p[0], nullptr);
return 0;
}
先申请信号量再加锁。