Linux多线程(信号量与环形队列)

目录

一、信号量

1.信号量概念

2.信号量系统接口

(1)sem_init

(2)sem_destroy

(3)sem_wait

(4)sem_post

二、环形队列

三、利用信号量和环形队列来实现生产者消费者模型

1.实现思路

(1)同步与互斥

(2)规则

(3)信号量思路

2.具体实现

(1)ring_queue.hpp

(2)ring_cp.cc


一、信号量

1.信号量概念

在学习SystemV的时候,就提到过信号量这一概念。

信号量的本质就是一个计数器,用来描述临界资源数目的大小(即有多少资源可以分配给线程)。使用信号量的本质其实就是预定资源(预定后才能使用)。可以通过信号量让不同的线程访问临界资源的不同区域,从而实现并发。

2.信号量系统接口

如果我们使用普通的计数器count来记录临界资源,显然count也是临界资源,因此是不安全的。所以OS提供了一系列与信号量有关的接口:

(1)sem_init

用于对信号量进行初始化,第一个参数为执行一个信号量的指针,第二个变量代表进程之间是否共享,这里设为0即可。第三个参数表示信号量的数量,需要我们自己设置。

(2)sem_destroy

即销毁信号量,参数为要销毁的信号量。

(3)sem_wait

sem_wait操作执行的是信号量--的操作:

用伪代码表示:

start;
lock();
if(count<=0)
{
wait;//挂起
}
else
{
count--;
}
(4)sem_post

sem_post执行的是信号量++的操作。

它的伪代码如下:

lock();
count++;
unlock();

二、环形队列

唤醒列表最早在数据结构哪里学过,我们使用数组来模拟环形队列,当遍历到数组结尾的时候,回到数组首元素位置。

当拿和放指向同一个位置的时候,环形队列既可能是空,也可能是满。因此我们需要做一个判断。

1.可以定义一个镂空的位置,当当前+1位置可以放,队列为空;否则队列已满,不再放。

2.可以定义一个计数器,每放一次就++,每拿一次就--,根据计数器的值来判断队列是否已满。

三、利用信号量和环形队列来实现生产者消费者模型

使用信号量和环形队列的好处在于,可以让消费者和生产者可以不同时访问同一块临界资源,因此不需要加锁。

1.实现思路

(1)同步与互斥

当生产者和消费者指向同一个位置的时候,队列可能为空也可能为满,因此当队列不为空,不为满的时候生产者和消费者指向的不是同一个位置。因此生产和消费可以并发执行,不用加锁,符合互斥特性。

当队列为空时,生产者执行,队列未满时,消费者执行,这也体现了局部上的同步特性。

(2)规则

生产者关心的是环形队列中空的位置,消费者关心的是环形队列总的数据。

1.生产者不能对消费者扣圈。

2.消费者不能超过生产者。

3.当指向同一个位置的时候,要根据空或者满的状态来判断让谁先执行。

(3)信号量思路

P操作:消耗(消费者)

V操作:生产(生产者)

当生产者生产数据后,blank信号量--,data信号量++。当有data后,消费者开始消耗数据,即data信号量--,blank信号量++。从而实现换装结构。

2.具体实现

(1)ring_queue.hpp
#include<iostream>
#include<vector>
#include<pthread.h>
#include<semaphore.h>
#include<unistd.h>
#include<stdlib.h>
using namespace std;
namespace ns_ring_queue
{
    template<class T>
    class RingQueue
    {
    private:
        vector<T> ring_queue_;
        int cap_;
        sem_t blank_sem_;//空格资源信号量
        sem_t data_sem_;//数据资源信号量
        int c_step_;//消费者位置
        int p_step_;//生产者位置
        pthread_mutex_t con;
        pthread_mutex_t pro;
    public:
        RingQueue(int cap=10):cap_(cap),ring_queue_(cap)
        {
            sem_init(&blank_sem_,0,cap);
            sem_init(&data_sem_,0,0);
            c_step_=p_step_=0;
        }
        void Push(const T& in)
        {
            sem_wait(&blank_sem_);
            pthread_mutex_lock(&pro);
            ring_queue_[p_step_]=in;
             p_step_++;
            p_step_%=cap_;
            pthread_mutex_unlock(&pro);
            sem_post(&data_sem_);
        }
        void Pop(T* out)
        {
            sem_wait(&data_sem_);
            pthread_mutex_lock(&con);
            *out=ring_queue_[c_step_];
            c_step_++;
            c_step_%=cap_;
            pthread_mutex_unlock(&con);
            sem_post(&blank_sem_);
        }
        ~RingQueue()
        {
            sem_destroy(&blank_sem_);
            sem_destroy(&data_sem_);
        }    
    };
}
(2)ring_cp.cc
#include"ring_queue.hpp"
using namespace std;
using namespace ns_ring_queue;
void* consumer(void* args)
{
    RingQueue<int>* rq=(RingQueue<int>*)args;
    while(true)
    {
        int data=0;
        rq->Pop(&data);
        cout<<"正在被线程"<<pthread_self()<<"消费的数据是:"<<data<<endl;
        sleep(1);
    }
}
void* productor(void* args)
{
    RingQueue<int>* rq=(RingQueue<int>*)args;
    while(true)
    {
        int data=rand()%20+1;
        cout<<"正在被线程"<<pthread_self()<<"生产"<<"生产的数据是:"<<data<<endl;
        rq->Push(data);
    }
}
int main()
{
    RingQueue<int>* rq=new RingQueue<int>();
    pthread_t c1,c2,c3,c4,p1,p2,p3;
    pthread_create(&c1,nullptr,consumer,(void*)rq);
    pthread_create(&c2,nullptr,consumer,(void*)rq);
    pthread_create(&c3,nullptr,consumer,(void*)rq);
    pthread_create(&c4,nullptr,consumer,(void*)rq);
    pthread_create(&p1,nullptr,productor,(void*)rq);
    pthread_create(&p2,nullptr,productor,(void*)rq);
    pthread_create(&p3,nullptr,productor,(void*)rq);
    pthread_join(c1,nullptr);
    pthread_join(c2,nullptr);
    pthread_join(c3,nullptr);
    pthread_join(c4,nullptr);
    pthread_join(p1,nullptr);
    pthread_join(p2,nullptr);
    pthread_join(p3,nullptr);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值