目录
一、POSIX信号量介绍
1. 信号量原理
2,初始化信号量
3,信号量销毁
4,信号量等待
5,发布信号量
二,基于环形队列的生产消费模型
1.基于单线程
2,测试:
3,基于多线程
4,测试
三,代码链接
一、POSIX信号量介绍
1. 信号量原理
用于同步操作,以达到无冲突访问资源的目的,本质上起始就是一个计数器,用来描述临界资源中资源数目的计数器,处理有实现同步与互斥的功能之外,还可以更细粒度的管理临界资源。
资源划分:
可以的。临界区的资源也是可以按照小快区域进行划分的,这个由程序员自己控制,比如一个队列啊、或者一个数组啊等等,也可以自己实现划分。
信号量的本质是一个计数器,我们回忆一下锁:只要我申请到了锁,那么我哪怕不使用,那块资源也是我的。这就好比信号量,只要你申请到了信号量,那么你就一定属于你的资源,至于那一快资源,不知道,但是一定是有的,二元信号量==互斥锁。
2,初始化信号量
pshared:0表示线程间共享,非0表示进程间共享。
value:初始化信号量。
3,信号量销毁
销毁指定信号量。
4,信号量等待
相当于--信号量,也相当于申请信号量资源。
三个函数分别有不同的功能.一般第一个就够了。用到再去查文档。
5,发布信号量
相等于++信号量,也相当于归还资源。
二,基于环形队列的生产消费模型
和基于阻塞队列的生产消费模型差不多的,这里这里该用信号量和互斥锁来控制同步互斥关系。
1.基于单线程
ring_queue.hpp:基于
#pragma once
#include <iostream>
#include <pthread.h>
#include<unistd.h>
#include <vector>
#include <string>
#include <semaphore.h>
const int cap_ = 10;
using namespace std;
template <class T>
class ring_queue
{
public:
ring_queue(int cap = cap_)
:ringq_(cap),
spaceindex(0),
dataindex(0)
{
// 初始化信号量
sem_init(&space_sem_, 0, ringq_.size());
sem_init(&data_sem_, 0, 0);
// 初始化互斥锁
pthread_mutex_init(&space_mutex, nullptr);
pthread_mutex_init(&data_mutex, nullptr);
}
// 生产数据
void push(const T &in)
{
// 申请信号量
sem_wait(&space_sem_);
ringq_[spaceindex] = in;
spaceindex++;
spaceindex %= ringq_.size();
// 空间资源用掉一个,数据资源就增加一个。
sem_post(&data_sem_);
}
// 消费数据
T pop()
{
// 申请数据资源,信号量
sem_wait(&data_sem_);
T tmp = ringq_[dataindex];
dataindex++;
dataindex %= ringq_.size();
// 消耗一个数据资源,空间资源就会多一个
sem_post(&space_sem_);
return tmp;
}
private:
vector<T> ringq_; // 数组模拟环形队列。
sem_t space_sem_; // 记录空间资源个数
sem_t data_sem_; // 记录数据资源个数
int spaceindex; // 空间资源起始下标
int dataindex; // 数据资源起始下标
pthread_mutex_t space_mutex; // 多线程保护spaceindex++;
pthread_mutex_t data_mutex; // 多线程保护dataindex++;
};
ring_test.cpp
#include <ctime>
#include "ring_queue.hpp"
#include <vector>
using namespace std;
void *consumer(void *args)
{
ring_queue<int> *ringq = (ring_queue<int> *)args;
while (true)
{
int tmp = ringq->pop();
cout << "我是消费者,我的id:" << pthread_self() << "消费数据:" << tmp << endl;
sleep(1);
}
}
void *productor(void *args)
{
ring_queue<int> *ringq = (ring_queue<int> *)args;
while (true)
{
int data = rand() % 50;
ringq->push(data);
cout << "我是生产者,我的id:" << pthread_self() << "生产数据:" <<data << endl;
// sleep(1);
}
}
int main()
{
srand(time(0));
ring_queue<int> ringq(10);
pthread_t t1;
pthread_t t2;
pthread_t t3;
pthread_t t4;
pthread_t t5;
pthread_t t6;
pthread_create(&t1, nullptr, consumer, &ringq);
// pthread_create(&t2, nullptr, consumer, &ringq);
// pthread_create(&t3, nullptr, consumer, &ringq);
// pthread_create(&t4, nullptr, productor, &ringq);
// pthread_create(&t5, nullptr, productor, &ringq);
pthread_create(&t6, nullptr, productor, &ringq);
pthread_join(t1, nullptr);
// pthread_join(t2, nullptr);
// pthread_join(t3, nullptr);
// pthread_join(t4, nullptr);
// pthread_join(t5, nullptr);
pthread_join(t6, nullptr);
return 0;
}
2,测试:
我们让生产者快,消费慢
看到没有,生产者生产满了是会等消费者消费了再去生产的。
我们让消费者快,生产者慢
看到没有,消费者会等待线生者生产的,当数据资源==0时,生产消费时互斥的,只能生产者生产,看到没有,当数据资源满了时,也只能消费者消费,看懂了没有。
3,基于多线程
因为space_index++;data_index++:也不是原子操作,所以要加锁保护,其他都一样了。
ring_queue.hpp
#pragma once
#include <iostream>
#include <pthread.h>
#include <unistd.h>
#include <vector>
#include <string>
#include <semaphore.h>
const int cap_ = 10;
using namespace std;
template <class T>
class ring_queue
{
public:
ring_queue(int cap = cap_)
: ringq_(cap),
spaceindex(0),
dataindex(0)
{
// 初始化信号量
sem_init(&space_sem_, 0, ringq_.size());
sem_init(&data_sem_, 0, 0);
// 初始化互斥锁
pthread_mutex_init(&space_mutex, nullptr);
pthread_mutex_init(&data_mutex, nullptr);
}
// 生产数据
void push(const T &in)
{
// 申请信号量
sem_wait(&space_sem_);
pthread_mutex_lock(&space_mutex_);
ringq_[spaceindex] = in;
spaceindex++;
spaceindex %= ringq_.size();
pthread_mutex_unlock(&space_mutex_);
// 空间资源用掉一个,数据资源就增加一个。
sem_post(&data_sem_);
}
// 消费数据
T pop()
{
// 申请数据资源,信号量
sem_wait(&data_sem_);
pthread_mutex_lock(&data_mutex_);
T tmp = ringq_[dataindex];
dataindex++;
dataindex %= ringq_.size();
pthread_mutex_unlock(&data_mutex_);
// 消耗一个数据资源,空间资源就会多一个
sem_post(&space_sem_);
return tmp;
}
private:
vector<T> ringq_; // 数组模拟环形队列。
sem_t space_sem_; // 记录空间资源个数
sem_t data_sem_; // 记录数据资源个数
int spaceindex; // 空间资源起始下标
int dataindex; // 数据资源起始下标
pthread_mutex_t space_mutex_; // 多线程保护spaceindex++;
pthread_mutex_t data_mutex_; // 多线程保护dataindex++;
};
ring_test.cpp
#include <ctime>
#include "ring_queue.hpp"
#include <vector>
using namespace std;
void *consumer(void *args)
{
ring_queue<int> *ringq = (ring_queue<int> *)args;
while (true)
{
int tmp = ringq->pop();
cout << "我是消费者,我的id:" << pthread_self() << "消费数据:" << tmp << endl;
//sleep(1);
}
}
void *productor(void *args)
{
ring_queue<int> *ringq = (ring_queue<int> *)args;
while (true)
{
int data = rand() % 50;
ringq->push(data);
cout << "我是生产者,我的id:" << pthread_self() << "生产数据:" <<data << endl;
sleep(1);
// sleep(1);
}
}
int main()
{
srand(time(0));
ring_queue<int> ringq(10);
pthread_t t1;
pthread_t t2;
pthread_t t3;
pthread_t t4;
pthread_t t5;
pthread_t t6;
pthread_create(&t1, nullptr, consumer, &ringq);
pthread_create(&t2, nullptr, consumer, &ringq);
pthread_create(&t3, nullptr, consumer, &ringq);
pthread_create(&t4, nullptr, productor, &ringq);
pthread_create(&t5, nullptr, productor, &ringq);
pthread_create(&t6, nullptr, productor, &ringq);
pthread_join(t1, nullptr);
pthread_join(t2, nullptr);
pthread_join(t3, nullptr);
pthread_join(t4, nullptr);
pthread_join(t5, nullptr);
pthread_join(t6, nullptr);
return 0;
}
4,测试
这里的生产消费起始并发的,但是是广义上的并发,因为现实中生产数据,使用数据都是需要时间的。
三,代码链接
lyh_linux-test: linux代码练习。 - Gitee.com