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

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

代码

cpp 复制代码
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

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

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

线程池

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

代码

cpp 复制代码
#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函数

封装线程

代码

cpp 复制代码
#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_; //回调函数
};
相关推荐
小飞猪Jay28 分钟前
C++面试速通宝典——13
jvm·c++·面试
rjszcb1 小时前
一文说完c++全部基础知识,IO流(二)
c++
韩楚风2 小时前
【linux 多进程并发】linux进程状态与生命周期各阶段转换,进程状态查看分析,助力高性能优化
linux·服务器·性能优化·架构·gnu
陈苏同学2 小时前
4. 将pycharm本地项目同步到(Linux)服务器上——深度学习·科研实践·从0到1
linux·服务器·ide·人工智能·python·深度学习·pycharm
Ambition_LAO2 小时前
解决:进入 WSL(Windows Subsystem for Linux)以及将 PyCharm 2024 连接到 WSL
linux·pycharm
小字节,大梦想2 小时前
【C++】二叉搜索树
数据结构·c++
吾名招财2 小时前
yolov5-7.0模型DNN加载函数及参数详解(重要)
c++·人工智能·yolo·dnn
Pythonliu72 小时前
茴香豆 + Qwen-7B-Chat-Int8
linux·运维·服务器
你疯了抱抱我2 小时前
【RockyLinux 9.4】安装 NVIDIA 驱动,改变分辨率,避坑版本。(CentOS 系列也能用)
linux·运维·centos
追风赶月、2 小时前
【Linux】进程地址空间(初步了解)
linux