波奇学Linux:信号量环形队列,原生线程池,封装线程

本文详细介绍了如何使用信号量和锁来实现一个线程安全的多生产多消费环形队列,以及在多线程环境中如何确保同步和资源管理。同时探讨了线程池的设计,包括任务的推送和消费,以及如何在类中正确封装和调用处理任务的函数。
摘要由CSDN通过智能技术生成

 基于信号量的多生产多消费环形队列

代码

const static int defaultcap=5;

template<class T>
class RingQueue
{
private:
    void P(sem_t &sem)
    {
        sem_wait(&sem); //资源-1
    }
    void V(sem_t &sem)
    {
        sem_post(&sem); //资源加一
    }
    void Lock(pthread_mutex_t &mutex)
    {
        pthread_mutex_lock(&mutex);
    }
    void Unlock(pthread_mutex_ &mutex)
    {
        pthread_mutex_unlock(&mutex);
    }
public:
    RingQueue(int cap=defaultcap)
    :ringqueue_(cap),cap_(cap),c_step_(0),p_step_(0)
    {
        sem_init(&cdata_sem_,0,0); //初始化信号量
        sem_init(&pspace_sem_,0,cap_); //初始化信号量
        pthread_mutex_init(&c_mutex_,nullptr);
        pthread_mutex_init(&c_mutex_,nullptr);
    }
    void Push(const T&in) //生产
    {
        //先申请信号量后申请锁
        //能让两个申请并行,提高并发度
        //申请信号量是原子的
        P(pspace_sem_); 
        Lock(p_mutex_);
        ringqueue_[p_step_]=in;
         // 位置后移,维持环形特征
        p_step_++;
        p_step_%=cap_;
        
        Unlock(p_mutex_);
        V(cdata_sem_);
    }
    void Pop(T* out) //消费
    {
        P(cdata_sem_);
        Lock(c_mutex_);
        *out=ringqueue_[c_step_];
         // 位置后移动
        c_step_++;
        c_step_%=cap_;
        Unlock(c_mutex_);
        V(pspace_sem_);
    
    }
    ~RingQueue()
    {
        sem_destroy(&cdata_sem_); //释放信号量资源
        sem_destroy(&pspace_sem_);
        pthread_mutex_destroy(&c_mutex_);
        pthread_mutex_destroy(&p_mutex_);
    }
private:
    std::vector<T> ringqueue_;
    int cap_;

    int c_step_; //消费者下标
    int p_step_; // 生产者下标
    // 使用信号量可以自动维护互斥性
    sem_t cdata_sem_; //消费者关注的数据资源
    sem_t pspace_sem_; // 生产者关注的空间资源

    pthread_mutex_t c_mutex_; // 多个生产者竞争对信号量的申请
    pthread_mutex_t p_mutex_; // 多个消费者竞争对信号量的申请
};

信号量保证生产者和消费者的同步关系

多线程就要上锁

问题思考 

当多线程访问时如何,锁和信号量谁在前?

信号量申请在前,原因可以使得申请信号量和申请锁行为同步,申请信号量是原子的

当多个线程持有信号量时,会不会对消费资源进行误导?

不会,因为资源的消费取决于cdata_sem_,而当释放c_data_sem此时的确实已经表明资源量加1

当不会出现队列只剩下一个空位,但是有多个线程持有信号量?

不会,因为信号量和空位一一对应的,如果能申请到信号量说明确实有空位

线程池 

接收任务,线程池里面的多个线程分配任务

代码

#pragma once

#include<iostream>
#include<pthread.h>
#include<vector>
#include<queue>
#include<string>
#include<unistd.h>

struct ThreadInfo
{
    pthread_t tid;
    std::string name;
};
static const int defaultnum=5;

template<class T>
class ThreadPool
{
private:
    void Lock()
    {
        pthread_mutex_lock(&mutex_);
    }
    void Unlock()
    {
        pthread_mutex_unlock(&mutex_);
    }
    void Wakeup() //唤醒进程
    {
        pthread_cond_signal(&cond_);
    }
    void ThreadSleep()
    {
        pthread_cond_wait(&cond_,&mutex_);
    }
    bool IsQueueEmpty()
    {
        return tasks_.empty();
    }
public:
    ThreadPool(int num=defaultnum)
    :threads_(num)
    {
        pthread_mutex_init(&mutex_,nullptr);
        pthread_cond_init(&cond_,nullptr);
    }
    //HandlerTask放在类里面会多出一个指针,要加上static
    static void* HandlerTask(void*args)
    {
        ThreadPool<T>*tp=static_cast<ThreadPool<T>*>(args);
        while(true)
        {
            tp->Lock();
            while(tp->IsQueueEmpty())
            {
                tp->ThreadSleep();
            }
            // T t=tp->Pop();
            
            tp->Unlock();
            // t(); //处理任务的同时不涉及共享资源,可以并发指行
        }
    }
    //初始化,创建多个线程
    void Start()
    {
        int num=threads_.size();
        for(int i=0;i<num;i++)
        {
            threads_[i].name="thread-"+std::to_string(i+1);
            pthread_create(&(threads_[i].tid),nullptr,HandlerTask,this);
        }
    }

    void Push(const T&t)
    {
        Lock();
        tasks_.push(t);
        Wakeup();
        Unlock();
    }
    T Pop()
    {
        T t=tasks_.front();
        tasks_.pop();
        return t;
    }
    ~ThreadPool()
    {
        pthread_mutex_destroy(&mutex_);
        pthread_cond_destroy(&cond_);
    }
private:
    std::vector<ThreadInfo> threads_;
    std::queue<T> tasks_;
    pthread_mutex_t mutex_;
    pthread_cond_t cond_;

};

问题思考

在类里面调用HandlerTask函数

封装线程

代码

#pragma once
#include<iostream>
#include<string>
#include<ctime>
#include<pthread.h>
typedef void (*callback_t)();
static int num=1;
class Thread
{
public:
    static void *Routine(void*args)
    {
        Thread* thread=static_cast<Thread*>(args);
        thread->Entery();
        return nullptr;
    }
public:
    Thread(callback_t cb)
    :tid_(0)
    ,name_("")
    ,start_timestamp_(0)
    ,isrunning_(false)
    ,cb_(cb)
    {

    }
    void Run()
    {
        name_="thread-" + std::to_string(num++);
        start_timestamp_=time(nullptr);
        isrunning_=true;
        pthread_create(&tid_,nullptr,Routine,this);
    }
    void Join()
    {
        pthread_join(tid_,nullptr);
        isrunning_=false;
    }
    std::string Name();
    uint64_t StartTimestamp();
    bool IsRunning()
    {
        return isrunning_;

    }
    void Entery()
    {
        cb_();
    }
    ~Thread()
    {

    }
private:
    pthread_t tid_; //线程tid
    std::string name_; //线程名字
    uint64_t start_timestamp_; //线程启动时间
    bool isrunning_; // 线程是否运行
    callback_t cb_; //回调函数
};

  • 8
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值