信号量(也叫信号灯),本质是一个在描述临界资源中,有效资源个数的计数器。
如果把临界区划分成多个块儿,那么多个线程可以同时进入临界区访问不同的块。
之前的锁,是我们认为临界资源只有一块。
信号量可以保证让多个线程同时进入临界区,访问临界资源的不同块。
申请信号量,把计数器--
叫做P操作;释放信号量,计数器++
叫做V操作。
要保证所有的线程都得看到信号量,所以信号量本身就是临界资源,因此得保证它的安全。
因此PV操作一定是原子的。
临界资源可以划分成多块。
这样就可以并行的访问临界资源,提高效率。
POSIX信号量和SystemV信号量作用相同,都是用于同步操作,达到无冲突的访问共享资源的目的。 但POSIX可以用于线程间同步。
#include
int sem_init(sem_t *sem, int pshared, unsigned int value);
pshared:0表示线程间共享,非零表示进程间共享
value:信号量初始值
int sem_destroy(sem_t *sem);
int sem_wait(sem_t *sem);
int sem_post(sem_t *sem);
环形队列可以抽象成数组,用模运算来模拟环状特性。
生产者和消费者不指向同一个位置时,队列不满不空;而指向同一个位置时,队列是满或空的状态。
也就是说,下标不同时,它们并行(同步);相同时,它们串行。(互斥)
该模型用信号量维持了生产者和消费者的同步与互斥。
#pragma once
#include
#include
#include
#include
#define NUM 10
class RingQueue{
private:
void P(sem_t &s) // 引用
{
sem_wait(&s); // 取地址
}
void V(sem_t &s)
{
sem_post(&s);
}
public:
RingQueue(int _cap = NUM):max_cap(_cap), v(_cap)
{
sem_init(&sem_space, 0, max_cap); // 0 代表线程间共享,max_cap信号量初始值
sem_init(&sem_data, 0, 0); // 数据资源最初为0
c_index = 0;// 默认消费者从0开始走
p_index = 0;// 默认生产者从0开始走
}
void Put(const int &in)
{
P(sem_space); // 生产者,申请格子资源
v[p_index] = in; // 生产
p_index++;
p_index %= max_cap;
V(sem_data); // 多了一份数据资源
}
void Get(int &out)
{
P(sem_data); // 消费者,申请数据资源
out = v[c_index]; // 消费
c_index++;
c_index %= max_cap;
V(sem_space); // 消费完就多了一份格子资源
}
~RingQueue()
{
sem_destroy(&sem_space);
sem_destroy(&sem_data);
c_index = 0;
p_index = 0;
}
private:
int max_cap;
std::vector<int> v;
sem_t sem_space; // 生产者关心的格子资源
sem_t sem_data; // 消费者关心的数据资源
int c_index; // 决定了它使用临界资源的哪一份
int p_index;
};
#include"RingQueue.hpp"
void* producer(void* ring_queue)
{
RingQueue* rq = (RingQueue*)ring_queue;
while(true)
{
int count = rand()%100 + 1;
sleep(1);
rq->Put(count); //生产
std::cout<< "producer is producing" << std::endl;
}
}
void* consumer(void* ring_queue)
{
RingQueue* rq = (RingQueue*)ring_queue;
while(true)
{
int data = 0;
rq->Get(data); // 消费
std::cout<< "consumer data: #" << data << std::endl;
}
}
int main()
{
pthread_t p,c;
RingQueue *rq = new RingQueue();
pthread_create(&p, nullptr, producer, rq);
pthread_create(&c, nullptr, consumer, rq);
pthread_join(p, nullptr);
pthread_join(c, nullptr);
delete rq;
return 0;
}
testRingQueue:main.cc
g++ $^ -o $@ -std=c++11 -lpthread
.PHONY:clean
clean:
rm -f testRingQueue