【Linux系统】POSIX信号量 && 线程池

PISIX信号量

概念

POSIX信号量和SystemV信号量作用相同,都是用于同步操作,达到无冲突的访问共享资源目的。 但POSIX可以用于线程间同步。

引入环形队列的概念 

 

 环形队列:当队列为空||为满时

head == end,我们发现这样无法区分为空为满两种情况

所以:我们需要1.计数器 2.牺牲一个空位置,判满就变为if(head == end + 1)

 如果队列不为空不为满就是head != end

问题:多线程如何在环形队列中进行生产和消费1.单生产,单消费 2.多生产,多消费

1.队列为空,让谁先访问?生产者先生产

2.队列为满,让谁先访问?消费者来消费

3.队列不为空&&队列不为满,生产和消费同时进行!!!(并发的!!!)

第三点要满足条件:a.不然生产者把消费者套一个圈b.不能让消费者超过生产者

所以前面两点不仅要有顺序,还要互斥。

三点放在一起就是互斥与同步的!!! ---- 所以这些信号量就能做到这些!!!

消费者只关心的资源,数据资源!生产者只关心的资源,空间资源!!!

sem_t data_sem = 0;
sem_t space_sem = N;

 生产与消费的关系是通过PV操作来实现的!

//生产者
P(space_sem);
//生产
V(data_sem);

//消费者
P(data_sem);
//消费
V(space_sem);

实现

 信号量接口

初始化信号量

#include <semaphore.h>
int sem_init(sem_t *sem, int pshared, unsigned int value);
  • sem:指向信号量 的指针
  • pshared:0表示线程间共享,非零表示进程间共享
  • value:信号量初始值 

 销毁信号量

int sem_destroy(sem_t *sem);

等待信号量(P操作)

功能:等待信号量,会将信号量的值减1

会将信号量的值减一,如果信号量的值为零,调用线程将被阻塞,直到信号量的值大于零

int sem_wait(sem_t *sem); //P()

发布信号量 (V操作)

功能:发布信号量,表示资源使用完毕,可以归还资源了。将信号量值加1。

会将信号量的值加一,如果有任何线程在等待该信号量,它们将被唤醒。

int sem_post(sem_t *sem);//V()

基于环形队列的生产消费模型

生产消费模型的高效率核心问题是:构建数据和后来的处理数据实现了多线程并发执行!!!因为这两个部分是最耗时的!

  • 环形队列采用数组模拟,用模运算来模拟环状特性
  • 环形结构起始状态和结束状态都是一样的,不好判断为空或者为满,所以可以通过加计数器或者标记位来判断满或者空。另外也可以预留一个空的位置,作为满的状态
  • 但是我们现在有信号量这个计数器,就很简单的进行多线程间的同步过程

RingQueue.hpp

#pragma once
#include<iostream>
#include<vector>
#include<semaphore.h>
template<class T>
class RingQueue
{
    void P(sem_t& s)
    {
        sem_wait(&s);
    }
    void V(sem_t& s)
    {
        sem_post(&s);
    }
public:
    RingQueue(int max_cap):_max_cap(max_cap), _ring_queue(max_cap), _c_step(0), _p_step(0)
    {
        sem_init(&_data_sem, 0, 0);
        sem_init(&_space_sem, 0, max_cap);

        pthread_mutex_init(&_c_mutex, nullptr);
        pthread_mutex_init(&_p_mutex, nullptr);
    }
    void Pop(T* out)//消费
    {
        //信号量:信号量是一个计数器,是资源的预订机制。预订:在外部,可以不判断资源是否满足,就可以知道内部资源的情况。
        P(_data_sem);//信号量这里,对资源进行使用,申请,为什么不判断一下条件是否满足???信号量本身就是判断条件!!!
        pthread_mutex_lock(&_c_mutex);
        *out = _ring_queue[_c_step];
        _c_step++;
        _c_step %= _max_cap;
        pthread_mutex_unlock(&_c_mutex);
        V(_space_sem);
    }
    void Push(const T &in)//生产
    {
        P(_space_sem);
        pthread_mutex_lock(&_p_mutex);
        _ring_queue[_p_step] = in;
        _p_step++;
        _p_step %= _max_cap;
        pthread_mutex_unlock(&_p_mutex);
        V(_data_sem);
    }
    ~RingQueue()
    {
        sem_destroy(&_data_sem);
        sem_destroy(&_space_sem);

        pthread_mutex_destroy(&_c_mutex);
        pthread_mutex_destroy(&_p_mutex);
    }
private:
    std::vector<T> _ring_queue;
    int _max_cap;

    int _c_step;
    int _p_step;

    sem_t _data_sem;//消费者关心
    sem_t _space_sem;//生产者关心

    pthread_mutex_t _c_mutex;
    pthread_mutex_t _p_mutex;
};

线程池

一种线程使用模式。线程过多会带来调度开销,进而影响缓存局部性和整体性能。而线程池维护着多个线程,等待着监督管理者分配可并发执行的任务。这避免了在处理短时间任务时创建与销毁线程的代价。线程池不仅能够保证内核的充分利用,还能防止过分调度。可用线程数量应该取决于可用的并发处理器、处理器内核、内存、网络sockets等的数量。

 下面我们使用打印日志的方式,并且运用单例模式中的懒汉模式实现了一个简单的线程池!!!

ThreadPool.hpp --- 线程池主逻辑封装

#pragma once
#include <iostream>
#include <queue>
#include <vector>
#include <string>
#include <unistd.h>
#include "Thread.hpp"
#include "Task.hpp"
#include "Log.hpp"

using namespace ThreadMoudle;
using namespace log_ns;
static const int gdefaultnum = 5;
void test()
{
    while (true)
    {
        std::cout << "hello world" << std::endl;
        sleep(1);
    }
}
template <typename T>
class ThreadPool
{
private:
    void LockQueue()
    {
        pthread_mutex_lock(&_mutex);
    }
    void UnlockQueue()
    {
        pthread_mutex_unlock(&_mutex);
    }
    void Wakeup()
    {
        pthread_cond_signal(&_cond);
    }
    void WakeupAll()
    {
        pthread_cond_broadcast(&_cond);
    }
    void Sleep()
    {
        pthread_cond_wait(&_cond, &_mutex);
    }
    bool IsEmpty()
    {
        return _task_queue.empty();
    }
    void HandlerTask(const std::string &name) // this
    {
        while (true)
        {
            // 取任务
            LockQueue();
            while (IsEmpty() && _isrunning)
            {
                _sleep_thread_num++;
                LOG(INFO, "%s thread sleep begin!\n", name.c_str());
                Sleep();
                LOG(INFO, "%s thread wakeup begin!\n", name.c_str());
                _sleep_thread_num--;
            }
            // 只需判断一种情况
            if (IsEmpty() && !_isrunning)
            {
                UnlockQueue();
                LOG(INFO, "%s thread quie!\n", name.c_str());
                break;
            }
            // 有任务
            T t = _task_queue.front();
            _task_queue.pop();
            UnlockQueue();
            // 处理任务,此处不用/不能在临界区中处理
            t();
            // std::cout << name << ":" << t.result() << std::endl;
            LOG(DEBUG, "hander task done, task is:%s\n", t.result().c_str());
        }
    }
    void Init()
    {
        func_t func = std::bind(&ThreadPool::HandlerTask, this, std::placeholders::_1);
        for (int i = 0; i < _thread_num; i++)
        {
            std::string threadname = "thread-" + std::to_string(i + 1);
            _threads.emplace_back(threadname, func);
            LOG(DEBUG, "construct thread %s done, init success!\n", threadname.c_str());
        }
    }
    void Start()
    {
        _isrunning = true;
        for (auto &thread : _threads)
        {
            LOG(DEBUG, "start thread %s done.\n", thread.Name().c_str());
            thread.Start();
        }
    }
    ThreadPool(int thread_num = gdefaultnum) : _thread_num(thread_num), _isrunning(false), _sleep_thread_num(0)
    {
        pthread_mutex_init(&_mutex, nullptr);
        pthread_cond_init(&_cond, nullptr);
    }
    ThreadPool(const ThreadPool<T> &) = delete;
    ThreadPool<T> operator=(const ThreadPool<T> &) = delete;

public:
    void Stop()
    {
        LockQueue();
        _isrunning = false;
        WakeupAll();
        UnlockQueue();
        LOG(DEBUG, "Thread Pool Stop Success!\n");
    }
    // 如果是多线程获取单例呢?
    static ThreadPool<T> *GetInstance()
    {
        LockGuard lockguard(&_sig_mutex);
        if (_tp == nullptr)
        {
            if (_tp == nullptr)
            {
                LOG(INFO, "create threadpool!\n");
                // thread-1,thread-2...
                _tp = new ThreadPool();
                _tp->Init();
                _tp->Start();
            }
            else
            {
                LOG(INFO, "get threadpool!\n");
            }
        }
        return _tp;
    }
    void Equeue(const T &in)
    {
        LockQueue();
        if (_isrunning)
        {
            _task_queue.push(in);
            if (_sleep_thread_num > 0)
                Wakeup();
        }
        UnlockQueue();
    }
    ~ThreadPool()
    {
        pthread_mutex_destroy(&_mutex);
        pthread_cond_destroy(&_cond);
    }

private:
    int _thread_num;
    std::vector<Thread> _threads;
    std::queue<T> _task_queue;
    bool _isrunning;

    int _sleep_thread_num;

    pthread_mutex_t _mutex;
    pthread_cond_t _cond;

    // 单例模式
    static ThreadPool<T> *_tp;
    static pthread_mutex_t _sig_mutex;
};

template <class T>
ThreadPool<T> *ThreadPool<T>::_tp = nullptr;
template <class T>
pthread_mutex_t ThreadPool<T>::_sig_mutex = PTHREAD_MUTEX_INITIALIZER;

Thread.hpp --- 封装了我们自己的线程库

#pragma once
#include<iostream>
#include<string>
#include<pthread.h>
#include<functional>

namespace ThreadMoudle
{
    //线程要执行的方法
    //typedef void (*func_t)(ThreadData *td);
    using func_t = std::function<void(const std::string&)>;
    class Thread
    {
    public:
        void Excute()
        {
            _isrunning = true;
            _func(_name);
            _isrunning = false;
        }
    public:
        Thread(const std::string& name, func_t func) :_name(name), _func(func)
        {
        }
        static void* ThreadRoutine(void* args)//新线程都会执行该方法
        {
            Thread* self = static_cast<Thread*>(args);//获得了当前对象
            self->Excute();
            return nullptr;
        }
        bool Start()
        {
            int n = ::pthread_create(&_tid, nullptr, ThreadRoutine, this);
            if(n != 0) return false;
            return true;
        }
        std::string Status()
        {
            if(_isrunning) return "running";
            else return "sleep";
        }
        void Stop()
        {
            if(_isrunning)
            {
                ::pthread_cancel(_tid);
                _isrunning = false;
            }
        }
        std::string Name()
        {
            return _name;
        }
        void Join()
        {
            ::pthread_join(_tid, nullptr);
        }
        ~Thread()
        {

        }
    public:
        std::string _name;
        pthread_t _tid;
        bool _isrunning;
        func_t _func; //线程要执行的回调函数
    };   
};

main.cc --- 不断推送任务到线程池并且测试

#include"ThreadPool.hpp"
#include"Task.hpp"
#include"Log.hpp"

using namespace log_ns;

int main()
{
    EnableScreen();
    // LOG(DEBUG, "hello %d, world: %c, hello: %f\n", 1000, 'A', 3.14);
    // LOG(WARNING, "hello %d, world: %c, hello: %f\n", 1000, 'A', 3.14);
    // LOG(FATAL, "hello %d, world: %c, hello: %f\n", 1000, 'A', 3.14);
    // LOG(ERROR, "hello %d, world: %c, hello: %f\n", 1000, 'A', 3.14);
    // EnableFile();
    // LOG(DEBUG, "hello %d, world: %c, hello: %f\n", 1000, 'A', 3.14);
    // LOG(DEBUG, "hello %d, world: %c, hello: %f\n", 1000, 'A', 3.14);
    // LOG(DEBUG, "hello %d, world: %c, hello: %f\n", 1000, 'A', 3.14);
    // LOG(DEBUG, "hello %d, world: %c, hello: %f\n", 1000, 'A', 3.14);

    // ThreadPool<Task>* tp = new ThreadPool<Task>();
    // tp->Init();
    // tp->Start();
    int cnt = 10;
    while(cnt)
    {
        //不断向线程池推送任务
        sleep(1);
        Task t(1, 1);
        ThreadPool<Task>::GetInstance()->Equeue(t);
        LOG(INFO, "equeue a task, %s\n", t.debug().c_str());
        sleep(1);
        cnt--;
    }
    ThreadPool<Task>::GetInstance()->Stop();
    LOG(INFO, "thread pool stop!\n");
    return 0;
}

Task.hpp --- 任务的来源我们这里是一个加法任务

 

#pragma once
#include<iostream>
#include<string>
#include<functional>

//typedef std::function<void()> task_t;
// using task_t = std::function<void()>;

// void Download()
// {
//     std::cout << "我是一个下载任务" << std::endl;
// }
class Task
{
public:
    Task()
    {}
    Task(int x, int y):_x(x), _y(y)
    {}
    void Excute()
    {
        _result = _x + _y;
    }
    std::string debug()
    {
        std::string msg = std::to_string(_x) + "+" + std::to_string(_y) + "=?";
        return msg;
    }
    void operator()()
    {
        Excute();
    }
    std::string result()
    {
        std::string msg = std::to_string(_x) + "+" + std::to_string(_y) + "=" + std::to_string(_result);
        return msg;
    }
private:
    int _x;
    int _y;
    int _result;
};

Log.hpp --- 封装一个我们自己的日志库
 

#pragma once
#include <iostream>
#include <sys/types.h>
#include <unistd.h>
#include <cstdarg>
#include <fstream>
#include <cstring>
#include <pthread.h>
#include <cstring>
#include "LockGuard.hpp"

namespace log_ns
{
    enum
    {
        DEBUG = 1,
        INFO,
        WARNING,
        ERROR,
        FATAL // 致命的
    };
    std::string LevelToString(int level)
    {
        switch (level)
        {
        case DEBUG:
            return "DEBUG";
        case INFO:
            return "INFO";
        case WARNING:
            return "WARNING";
        case ERROR:
            return "ERROR";
        case FATAL:
            return "FATAL";
        default:
            return "UNKNOWN";
        }
    }
    std::string GetCurrTime()
    {
        time_t now = time(nullptr);
        struct tm *curr_time = localtime(&now);
        char buffer[128];
        snprintf(buffer, sizeof(buffer), "%04d-%02d-%02d %02d:%02d:%02d",
                 curr_time->tm_year + 1900,
                 curr_time->tm_mon + 1,
                 curr_time->tm_mday,
                 curr_time->tm_hour,
                 curr_time->tm_min,
                 curr_time->tm_sec);
        return buffer;
    }
    class logmessage
    {
    public:
        std::string _level;
        pid_t _id;
        std::string _filename;
        int _filenumber;
        std::string _curr_time;
        std::string _message_info;
    };

#define SCREEN_TYPE 1
#define FILE_TYPE 2

    const std::string glogfile = "./log.txt";
    pthread_mutex_t glock = PTHREAD_MUTEX_INITIALIZER;

    class log
    {
    public:
        log(const std::string &logfile = glogfile) : _logfile(logfile), _type(SCREEN_TYPE)
        {
        }
        void Enable(int type)
        {
            _type = type;
        }
        void FlushLogToScreen(const logmessage &lg)
        {
            printf("[%s][%d][%s][%d][%s] %s",
                   lg._level.c_str(),
                   lg._id,
                   lg._filename.c_str(),
                   lg._filenumber,
                   lg._curr_time.c_str(),
                   lg._message_info.c_str());
        }
        void FlushLogToFile(const logmessage &lg)
        {
            std::ofstream out(_logfile, std::ios::app);
            if (!out.is_open())
                return;
            char logtxt[2048];
            snprintf(logtxt, sizeof logtxt, "[%s][%d][%s][%d][%s] %s",
                     lg._level.c_str(),
                     lg._id,
                     lg._filename.c_str(),
                     lg._filenumber,
                     lg._curr_time.c_str(),
                     lg._message_info.c_str());
            out.write(logtxt, strlen(logtxt));
            out.close();
        }
        void FlushLog(const logmessage &lg)
        {
            LockGuard lockguard(&glock);
            switch (_type)
            {
            case SCREEN_TYPE:
                FlushLogToScreen(lg);
                break;
            case FILE_TYPE:
                FlushLogToFile(lg);
                break;
            }
        }
        void logMessage(std::string filename, int filenumber, int level, const char *format, ...)
        {
            logmessage lg;

            lg._level = LevelToString(level);
            lg._id = getpid();
            lg._filename = filename;
            lg._filenumber = filenumber;
            lg._curr_time = GetCurrTime();

            va_list ap;
            va_start(ap, format);
            char log_info[1024];
            vsnprintf(log_info, sizeof(log_info), format, ap);
            va_end(ap);
            lg._message_info = log_info;

            // 打印出来日志
            FlushLog(lg);
        }
        ~log()
        {
        }

    private:
        int _type;
        std::string _logfile;
    };

    log lg;

#define LOG(level, Format, ...)                                        \
    do                                                                 \
    {                                                                  \
        lg.logMessage(__FILE__, __LINE__, level, Format, ##__VA_ARGS__); \
    } while (0)
#define EnableScreen()          \
    do                          \
    {                           \
        lg.Enable(SCREEN_TYPE); \
    } while (0)
#define EnableFile()          \
    do                        \
    {                         \
        lg.Enable(FILE_TYPE); \
    } while (0)
};

 

  • 16
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

花影随风_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值