【Linux】系统编程基于环形队列生产者消费者模型(C++)

目录

【1】引入POSIX信号量

【1.1】初始化信号量

【1.2】销毁信号量

【1.3】等待信号量

【1.4】发布信号量

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

【2.1】生产消费模型打印数字模型

【2.2】生产消费模型计算公式模型

【2.3】生产消费模型计算公式加保存任务模型


【1】引入POSIX信号量

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

什么是信号量:信号量本身是一把计数器,只要拥有信号量,就在未来一定能够拥有临界资源的一部分,申请信号量的本质就是对临界资源种特定小块资源的预定机制,线程要访问临界资源种的某一区域,**需要先申请信号量,所有人必须要先看到信号量,**信号量本身必须是公共资源。

【计数器】

  • 递减or递增 sem_t sem = 10;

  • sem-- : 申请资源 - 必须保证操作的原子性 - P

  • sem++: 归还资源 - 必须保证操作的原子性 - V

【信号量核心操作】PV原语。

【1.1】初始化信号量

md-end-block 复制代码
#include <semaphore.h>
int sem_init(sem_t *sem, int pshared, unsigned int value);
// 参数:
// pshared:0表示线程间共享,非零表示进程间共享
// value:信号量初始值

【1.2】销毁信号量

md-end-block 复制代码
int sem_destroy(sem_t *sem);

【1.3】等待信号量

md-end-block 复制代码
// 功能:等待信号量,会将信号量的值减1
int sem_wait(sem_t *sem); //P()

【1.4】发布信号量

md-end-block 复制代码
// 功能:发布信号量,表示资源使用完毕,可以归还资源了。将信号量值加1。
int sem_post(sem_t *sem);//V()

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

环形队列采用数组模拟,用模运算来模拟环状特性。

环形结构起始状态和结束状态都是一样的,不好判断为空或者为满,所以可以通过加计数器或者标记位来判断满或者空。另外也可以预留一个空的位置,作为满的状态。

【2.1】生产消费模型打印数字模型

【makefile文件】

md-end-block 复制代码
cc=g++
standard=-std=c++11

ringQueue:RingQueue.cc
	$(cc) -o $@ $^ $(standard) -l pthread

.PHONY:clean
clean:
	rm -rf ringQueue

【RingQueue.hpp文件】

md-end-block 复制代码
#pragma once 
#include <iostream>
#include <vector>
#include <cassert>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>

static const int g_maxCapacity = 5;

/* 环形队列类 */
template <class T>
class RingQueue
{
public:
    /* 构造函数 */
    RingQueue(const int& capacity = g_maxCapacity)
        : _cyclicQueue(capacity)
        , _capacity(capacity)
    {
        // 对于生产者来说的初始化:关注空间资源,初始化也是空间的大小
        int pN = sem_init(&_pSpaceSem, 0, capacity);
        assert(pN == 0); (void)pN;
        // 对于消费者来说的初始化:关注数据资源,初始化肯定是0,因为没有数据
        int cN = sem_init(&_cDataSem, 0, 0);
        assert(cN == 0); (void)cN;

        // 位置清零
        _pSubcript = _cSubcript = 0;

        // 生产者锁初始化
        pthread_mutex_init(&_pMutex, nullptr);
        // 消费者锁初始化
        pthread_mutex_init(&_cMutex, nullptr);
    }

    /* 析构函数 */
    ~RingQueue() 
    {
        // 生产者信号量销毁
        sem_destroy(&_pSpaceSem);
        // 消费者信号量销毁
        sem_destroy(&_cDataSem);
        // 生产者锁销毁
        pthread_mutex_destroy(&_pMutex);
        // 消费者锁销毁
        pthread_mutex_destroy(&_cMutex);
    }
public:
    /* 生产任务 */
    void Push(const T& in) 
    {
        // 生产前:申请空间信号量,保证一定可以进行生产,空间信号量-1
        P(_pSpaceSem);

        // 加锁
        pthread_mutex_lock(&_pMutex);
        _cyclicQueue[_pSubcript++] = in;
        _pSubcript %= _capacity;
        // 解锁
        pthread_mutex_unlock(&_pMutex);

        // 生产完:占用一个空间,数据信号量+1
        V(_cDataSem);
    }

    /* 消费任务 */
    void Pop(T* out)
    {
        // 消费前:申请数据信号量,保证一定可以进行消费,数据信号量-1
        P(_cDataSem);

        // 加锁
        pthread_mutex_lock(&_pMutex);
        *out = _cyclicQueue[_cSubcript++];
        _cSubcript %= _capacity;
        // 解锁
        pthread_mutex_unlock(&_pMutex);

        // 消费完:少了一个空间,空间信号量+1
        V(_pSpaceSem);
    }

private:
    /* 等待信号量,会将信号量的值减1 */
    void P(sem_t& sem)
    {
        int n = sem_wait(&sem);
        assert(n == 0); (void)n;
    }

    /* 发布信号量,表示资源使用完毕,可以归还资源了。将信号量值加1 */
    void V(sem_t& sem)
    {
        int n = sem_post(&sem);
        assert(n == 0); (void)n;
    }

private:
    std::vector<T>  _cyclicQueue;   // 环形队列容器
    int             _capacity;      // 环形队列容量
    int             _pSubcript;     // 生产者脚步(下标)
    int             _cSubcript;     // 消费者脚步(下标)
    sem_t           _pSpaceSem;     // 生产者信号量(生产者看重的是空间资源)
    sem_t           _cDataSem;      // 消费者信号量(消费者看重的是数据资源)
    pthread_mutex_t _pMutex;        // 生产者锁
    pthread_mutex_t _cMutex;        // 消费者锁
};

【RingQueue.cc文件】

md-end-block 复制代码
#include "RingQueue.hpp"
#include <cstdlib>
#include <ctime>



/* 生产者线程 */ 
void* ProducerRoutine(void* args)
{
    // 获取传递的线程数据
    RingQueue<int>* rq = static_cast<RingQueue<int>*>(args);

    while(true) 
    {
        sleep(1);
        // 生产者生产中.....
        int data = rand() % 100 + 1;
        rq->Push(data);
        std::cout << "生产完成,生产的数据是:" << data << std::endl;
    }
}

/* 消费者线程 */
void* ConsumerRoutine(void* args)
{
    // 获取传递的线程数据
    RingQueue<int>* rq = static_cast<RingQueue<int>*>(args);

    while(true) 
    {
        sleep(2);
        // 消费者消费中.....
        int data = 0;
        rq->Pop(&data);
        std::cout << "消费完成,消费的数据是:" << data << std::endl;
    }
}


/* 程序入口函数 */
int main() 
{
    // 定义随机数种子
    srand((unsigned int)time(nullptr) ^ getpid());
    // 线程数据
    RingQueue<int>* rq = new RingQueue<int>();
    
    // 定义生产者线程和消费者线程
    // 生产者:Producer 消费者:Consumer
    pthread_t tP, tC;
    pthread_create(&tP, nullptr, ProducerRoutine, (void*)rq);
    pthread_create(&tC, nullptr, ConsumerRoutine, (void*)rq);

    pthread_join(tP, nullptr);
    pthread_join(tC, nullptr);

    delete rq;
    return 0;
}

【2.2】生产消费模型计算公式模型

【makefile文件】

md-end-block 复制代码
cc=g++
standard=-std=c++11

ringQueue:RingQueue.cc
	$(cc) -o $@ $^ $(standard) -l pthread

.PHONY:clean
clean:
	rm -rf ringQueue

【RingQueue.hpp文件】

md-end-block 复制代码
#pragma once 
#include <iostream>
#include <vector>
#include <cassert>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>

static const int g_maxCapacity = 5;

/* 环形队列类 */
template <class T>
class RingQueue
{
public:
    /* 构造函数 */
    RingQueue(const int& capacity = g_maxCapacity)
        : _cyclicQueue(capacity)
        , _capacity(capacity)
    {
        // 对于生产者来说的初始化:关注空间资源,初始化也是空间的大小
        int pN = sem_init(&_pSpaceSem, 0, capacity);
        assert(pN == 0); (void)pN;
        // 对于消费者来说的初始化:关注数据资源,初始化肯定是0,因为没有数据
        int cN = sem_init(&_cDataSem, 0, 0);
        assert(cN == 0); (void)cN;

        // 位置清零
        _pSubcript = _cSubcript = 0;

        // 生产者锁初始化
        pthread_mutex_init(&_pMutex, nullptr);
        // 消费者锁初始化
        pthread_mutex_init(&_cMutex, nullptr);
    }

    /* 析构函数 */
    ~RingQueue() 
    {
        // 生产者信号量销毁
        sem_destroy(&_pSpaceSem);
        // 消费者信号量销毁
        sem_destroy(&_cDataSem);
        // 生产者锁销毁
        pthread_mutex_destroy(&_pMutex);
        // 消费者锁销毁
        pthread_mutex_destroy(&_cMutex);
    }
public:
    /* 生产任务 */
    void Push(const T& in) 
    {
        // 生产前:申请空间信号量,保证一定可以进行生产,空间信号量-1
        P(_pSpaceSem);

        // 加锁
        pthread_mutex_lock(&_pMutex);
        _cyclicQueue[_pSubcript++] = in;
        _pSubcript %= _capacity;
        // 解锁
        pthread_mutex_unlock(&_pMutex);

        // 生产完:占用一个空间,数据信号量+1
        V(_cDataSem);
    }

    /* 消费任务 */
    void Pop(T* out)
    {
        // 消费前:申请数据信号量,保证一定可以进行消费,数据信号量-1
        P(_cDataSem);

        // 加锁
        pthread_mutex_lock(&_pMutex);
        *out = _cyclicQueue[_cSubcript++];
        _cSubcript %= _capacity;
        // 解锁
        pthread_mutex_unlock(&_pMutex);

        // 消费完:少了一个空间,空间信号量+1
        V(_pSpaceSem);
    }

private:
    /* 等待信号量,会将信号量的值减1 */
    void P(sem_t& sem)
    {
        int n = sem_wait(&sem);
        assert(n == 0); (void)n;
    }

    /* 发布信号量,表示资源使用完毕,可以归还资源了。将信号量值加1 */
    void V(sem_t& sem)
    {
        int n = sem_post(&sem);
        assert(n == 0); (void)n;
    }

private:
    std::vector<T>  _cyclicQueue;   // 环形队列容器
    int             _capacity;      // 环形队列容量
    int             _pSubcript;     // 生产者脚步(下标)
    int             _cSubcript;     // 消费者脚步(下标)
    sem_t           _pSpaceSem;     // 生产者信号量(生产者看重的是空间资源)
    sem_t           _cDataSem;      // 消费者信号量(消费者看重的是数据资源)
    pthread_mutex_t _pMutex;        // 生产者锁
    pthread_mutex_t _cMutex;        // 消费者锁
};

【Task.hpp文件】

md-end-block 复制代码
#pragma once
#include <iostream>
#include <string>
#include <functional>

/* 计算任务 */
class CalTask
{
public:
    using func_t = std::function<int(int, int, char)>;

public:
    /* 构造函数 */
    CalTask() {}

    /* 构造函数 */
    CalTask(int x, int y, char op, func_t func)
        : _x(x), _y(y), _op(op), _callBalk(func)
    {
    }

public:
    /* 仿函数 */
    std::string operator()()
    {
        int result = _callBalk(_x, _y, _op);

        char buffer[64];
        snprintf(buffer, sizeof(buffer), "%d %c %d = %d\n", _x, _op, _y, result);
        return buffer;
    }

public:
    /* 返回打印公式 */
    std::string ToTaskString()
    {
        char buffer[64];
        snprintf(buffer, sizeof(buffer), "%d %c %d = ?\n", _x, _op, _y);
        return buffer;
    }

private:
    int _x;
    int _y;
    char _op;
    func_t _callBalk;
};

/* 执行计算的方法 */
int MyCalculate(int x, int y, char op)
{
    int result = 0;
    switch (op)
    {
    case '+':
        result = x + y;
        break;

    case '-':
        result = x - y;
        break;

    case '*':
        result = x * y;
        break;

    case '/':
    {
        if (y == 0)
        {
            std::cerr << "div zero error!" << std::endl;
            result = -1;
        }
        else
        {
            result = x / y;
        }
        break;
    }

    case '%':
    {
        if (y == 0)
        {
            std::cerr << "mod zero error!" << std::endl;
            result = -1;
        }
        else
        {
            result = x % y;
        }
        break;
    }

    default:
        break;
    }

    return result;
}

【RingQueue.cc文件】

md-end-block 复制代码
#include <cstdlib>
#include <ctime>
#include "RingQueue.hpp"
#include "Task.hpp"

static std::string oper = "+-*/%";
const std::string ThreadName()
{
    char buffer[64];
    snprintf(buffer, sizeof(buffer), "Thread[0x%x]", pthread_self());
    return buffer;
}


/* 生产者线程 */ 
void* ProducerRoutine(void* args)
{
    // 获取传递的线程数据
    RingQueue<CalTask>* rq = static_cast<RingQueue<CalTask>*>(args);

    while(true) 
    {
        sleep(1);
        // 生产者生产中.....
        int x = rand() % 100 + 1;
        int y = rand() % 100 + 1;
        char op = oper[rand() % oper.size()];
        CalTask t(x, y, op, MyCalculate);

        rq->Push(t);
        std::cout << ThreadName() << "生产者生产任务:" << t.ToTaskString() << std::endl;
    }
}

/* 消费者线程 */
void* ConsumerRoutine(void* args)
{
    // 获取传递的线程数据
    RingQueue<CalTask>* rq = static_cast<RingQueue<CalTask>*>(args);

    while(true) 
    {
        sleep(2);
        // 消费者消费中.....
        CalTask t;
        rq->Pop(&t);
        std::cout <<  ThreadName() << "消费者消费任务:" << t() << std::endl;
    }
}


/* 程序入口函数 */
int main() 
{
    // 定义随机数种子
    srand((unsigned int)time(nullptr) ^ getpid());
    // 线程数据
    RingQueue<CalTask>* rq = new RingQueue<CalTask>();
    
    // 定义生产者线程和消费者线程
    // 生产者:Producer 消费者:Consumer
    pthread_t tP, tC;
    pthread_create(&tP, nullptr, ProducerRoutine, (void*)rq);
    pthread_create(&tC, nullptr, ConsumerRoutine, (void*)rq);

    pthread_join(tP, nullptr);
    pthread_join(tC, nullptr);
    
    
     创建生产消费线程
    //pthread_t t_p[4];      // Producer(生产者)
    //pthread_t t_c[8];      // Consumer(消费者)
     pthread_create(&t_p, nullptr, ProducerRoutine, rq);
    //for(int i = 0; i < 4; i++)
    //{
    //    pthread_create(t_p + i, nullptr, ProducerRoutine, rq);
    //}
    //
     pthread_create(&t_c, nullptr, ConsumerRoutine, rq);
    //for(int i = 0; i < 8; i++)
    //{
    //    pthread_create(t_c + i, nullptr, ConsumerRoutine, rq);
    //}
	//
    //
	//
     线程等待
     pthread_join(t_p, nullptr);
    //for(int i = 0; i < 4; i++)
    //{
    //    pthread_join(t_p[i], nullptr);
    //}
     pthread_join(t_c, nullptr);
    //for(int i = 0; i < 8; i++)
    //{
    //    pthread_join(t_c[i], nullptr);
    //}

    delete rq;
    return 0;
}

【2.3】生产消费模型计算公式加保存任务模型

【Makefile文件】

md-end-block 复制代码
# 创建替换变量并且复制对应的含义
cc := g++
standard := -std=c++11

# 创建依赖关系
myThreadRingQueue: ThreadRingQueue.cc
	$(cc) -o $@ $^ $(standard) -l pthread

# 创建辅助命令
clean:
	rm -rf myThreadRingQueue 

.PHONY: clean

【ThreadBase.hpp】

md-end-block 复制代码
#pragma once  
#include <cstdio>
#include <cassert>
#include <iostream>
#include <functional>
#include <string>

#include <pthread.h>
class ThreadBase;

/* 线程上下文数据封装类 */
class ThreadBaseConnectText
{
public:
    ThreadBaseConnectText()
        : _textThis(nullptr)
        , _textArgs(nullptr)
    {}
public: 
    ThreadBase*  _textThis;
    void*        _textArgs;
};


/* 基于原生线程库的线程封装类 */
class ThreadBase
{
private:
	const int ctNum = 64;

public:
    // 定义仿函数
    using func_t = std::function<void*(void*)>;

public:

public:
    /* - 构造函数
     * - func: 线程回调函数
     * - args:线程回调函数参数
     * - num : 编写线程名称设定的编号 
     */
    ThreadBase(func_t func, void* args = nullptr, const int& num = 1)
        : _threadCallBack(func)
        , _threadArgs(args)
    {   
        // 自定义线程名称
        char nameBuffer[ctNum];
        snprintf(nameBuffer, sizeof(nameBuffer), "thread-%d", num);
        _threadName = nameBuffer;

        // 创建线程连接上下文 - 手动释放内存 - 【01】
        ThreadBaseConnectText* connectText = new ThreadBaseConnectText();
        connectText->_textThis = this;
        connectText->_textArgs = _threadArgs;

        int state = pthread_create(&_threadId, nullptr, StartRoutine, (void*)connectText);
        assert(state == 0); (void)state;
    }

    /* - 析构函数
     */
    ~ThreadBase() 
    {}

public:
    /* - 线程等待
     */
    void Join()
    {
        int state = pthread_join(_threadId, nullptr);
        assert(state == 0); (void)state;   
    }

public: 
	/* - 获取线程名称
	*/
	std::string GetThreadName()
	{
		return _threadName;
	}

	/* - 获取线程Id
	*/
	std::string GetThreadId()
	{
		char buffer[ctNum];
		snprintf(buffer, sizeof(buffer), "0x%x", _threadId);
		return buffer;
	}

public:
    /* - 线程函数
     */
    static void* StartRoutine(void* args)
    {
        ThreadBaseConnectText* connectText = static_cast<ThreadBaseConnectText*>(args);
        void* retVal = connectText->_textThis->Run(connectText->_textArgs);

        // 释放内存 - 【01】
        delete connectText;
        // 返回
        return retVal;
    }

private:
	/* - StartRoutine专用函数(因为C/C++混编的原因)
	*/
    void* Run(void* args)
    {
        // 调用回调线程
        return _threadCallBack(args);
    }

private:
    std::string        _threadName;         // 线程名称
    pthread_t          _threadId;           // 线程Id
    func_t             _threadCallBack;     // 线程回调函数
    void*              _threadArgs;         // 线程回调函数参数
};

【ThreadMutex.hpp】

md-end-block 复制代码
#pragma once 
#include <pthread.h>

/* 原生线程锁类封装 */
class Mutex
{
public:
    /* - 构造函数
     */
    Mutex(pthread_mutex_t* mutex)
        : _pMutex(mutex)
    {}

    /* - 析构函数
     */
    ~Mutex() 
    {}

public:
    /* - 加锁函数
     */
    void Lock() { pthread_mutex_lock(_pMutex); }
    /* - 解锁函数
     */
    void UnLock() { pthread_mutex_unlock(_pMutex); }

private:
    pthread_mutex_t*    _pMutex;    // 内部的线程锁
};

class LockGuardMutex
{
public:
    /* - 构造函数
     */
    LockGuardMutex(pthread_mutex_t* mutex)
        : _mutex(mutex)
    {
        _mutex.Lock();
    }
    
    /* - 析构函数
     */
    ~LockGuardMutex()
    {
        _mutex.UnLock();
    }
        
private:
    Mutex   _mutex;
};

【ThreadRingQueue.cc】

md-end-block 复制代码
#include <ctime>
#include <iostream>
#include <memory>
#include <unistd.h>
#include "ThreadRingQueue.hpp"
#include "ThreadBase.hpp"
#include "ThreadMutex.hpp"
#include "ThreadTask.hpp"

static std::string oper = "+-*/%";

/* - 生产者线程函数
 */
void* ProducerThread(void* args)
{
    sleep(1);
    ThreadRingQueue<TaskCalculate>* pRQ = (static_cast<ThreadRingQueues<TaskCalculate, TaskSave>*>(args))->_cTask;
    
    while(true)
    {
        int x = rand() % 100 + 1;
        int y = rand() % 100 + 1;
        char op = oper[rand() % oper.size()];
        TaskCalculate cTask(Calculate, x, y, op);

        pRQ->Push(cTask);
        std::cout << "生产者在生产任务-> " << "[" << cTask.TaskString() << "] - - 生产者当前位置:" << pRQ->GetPosP()  << std::endl;
        sleep(1);
    }

    return nullptr;
}

/* - 消费者线程函数
 */
void* ConsumerThread(void* args)
{
    sleep(1);
    ThreadRingQueue<TaskCalculate>* cRQ = (static_cast<ThreadRingQueues<TaskCalculate, TaskSave>*>(args))->_cTask;
    ThreadRingQueue<TaskSave>* sRQ = (static_cast<ThreadRingQueues<TaskCalculate, TaskSave>*>(args))->_sTask;

    while(true)
    {
        TaskCalculate cTask;
        cRQ->Pop(&cTask);
        std::string message = cTask();
        std::cout << "消费者在消费任务-> " << "[" << cTask() << "] - - 消费者当前位置:" << cRQ->GetPosC() << std::endl;
        
        TaskSave sTask(FileSave, message);

        sRQ->Push(sTask);
        std::cout << "ConsumerThread-推送保存完成..." << std::endl;
        sleep(3);
    }

    return nullptr;
}

/* - 保存者线程函数
 */
void* SaveThread(void* args)
{
    sleep(1);
    ThreadRingQueue<TaskSave>* sRQ = (static_cast<ThreadRingQueues<TaskCalculate, TaskSave>*>(args))->_sTask;
    
    while(true)
    {
        TaskSave sTask;
        sRQ->Pop(&sTask);
       
        sTask();
        std::cout << "SaveThread-保存任务完成..." << std::endl;
        sleep(3);
    }

    return nullptr;
}

/* = 程序入口函数
 */
int main()
{
    // 定义随机数种子
    srand((unsigned int)time(nullptr));
    // 定义共享资源
    ThreadRingQueues<TaskCalculate, TaskSave>* RQ = new ThreadRingQueues<TaskCalculate, TaskSave>();
    RQ->_cTask = new ThreadRingQueue<TaskCalculate>;
    RQ->_sTask = new ThreadRingQueue<TaskSave>;

    std::unique_ptr<ThreadBase> ptr_pTd(new ThreadBase(ProducerThread, (void*)RQ, 1));
    std::cout << "创建生产者线程完成-> 线程名:" << ptr_pTd->GetThreadName() << " 线程Id:" << ptr_pTd->GetThreadId() << std::endl;
    sleep(10);

    std::unique_ptr<ThreadBase> ptr_cTd(new ThreadBase(ConsumerThread, (void*)RQ, 2));
    std::cout << "创建生产者线程完成-> 线程名:" << ptr_cTd->GetThreadName() << " 线程Id:" << ptr_cTd->GetThreadId() << std::endl;
    sleep(5);

    std::unique_ptr<ThreadBase> ptr_sTd(new ThreadBase(SaveThread, (void*)RQ, 3));
    std::cout << "创建生产者线程完成-> 线程名:" << ptr_sTd->GetThreadName() << " 线程Id:" << ptr_sTd->GetThreadId() << std::endl;

    ptr_pTd->Join();
    ptr_cTd->Join();
    // ptr_sTd->Join();

    delete RQ->_cTask;
    delete RQ->_sTask;
    delete RQ;

    return 0;
}

【ThreadRingQueue.hpp】

md-end-block 复制代码
#pragma once 
#include <cstdio>
#include <cassert>
#include <iostream>
#include <vector>
#include <string>
#include <pthread.h>
#include <sys/types.h>
#include <semaphore.h>

static const size_t gCapacity = 10;

template <class T>
class ThreadRingQueue
{
public:
    /* - 构造函数
     */
    ThreadRingQueue(const size_t& capacity = gCapacity)
        : _capacity(capacity)
        , _v(capacity)
    {
        // 初始化信号量
        int n = 0;
        n = sem_init(&_pSem, 0, _capacity); assert(n == 0);
        n = sem_init(&_cSem, 0, _capacity); assert(n == 0);
        (void)n;

        // 生产者和消费者的位置初始化
        _pSubscript = _cSubscript = 0;

        // 初始化互斥锁
        pthread_mutex_init(&_pMutex, nullptr);
        pthread_mutex_init(&_cMutex, nullptr);
    }

    /* - 析构函数
     */
    ~ThreadRingQueue()
    {
        // 释放信号量
        sem_destroy(&_pSem);
        sem_destroy(&_cSem);

        // 释放互斥锁
        pthread_mutex_destroy(&_pMutex);
        pthread_mutex_destroy(&_cMutex);
    }

public:
    /* - 生产任务
     */
    void Push(const T& in)
    {
        // 生产前:保证可以生产->空间信号量-1
        P(_pSem);

        // 加锁
        pthread_mutex_lock(&_pMutex);

        _v[_pSubscript++] = in;
        _pSubscript %= _capacity;

        // 解锁
        pthread_mutex_unlock(&_pMutex);

        // 生产完:占用一个空间->数据信号量+1
        V(_cSem);
    }

    /* - 消费任务
     */    
    void Pop(T* out)
    {
        // 消费前:保证可以消费->数据信号量-1
        P(_cSem);

        // 加锁
        pthread_mutex_lock(&_pMutex);

        *out = _v[_cSubscript++];
        _cSubscript %= _capacity;

        // 解锁
        pthread_mutex_unlock(&_pMutex);
        
        // 消费完:减少一个空间->空间信号量+1
        V(_pSem);
    }

public:
    /* - 获取生产者当前位置
     */
    size_t GetPosP()
    {
        return _pSubscript;
    }

    /* - 获取消费者当前位置
     */
    size_t GetPosC()
    {
        return _cSubscript;
    }

private:
    /* - 等待信号量:会将信号量的值减1。
     */
    void P(sem_t& sem)
    {
        int n = sem_wait(&sem); assert(n == 0);
        (void)n;
    }

    /* - 发布信号量:表示资源使用完毕,可以归还资源了。将信号量值加1
     */
    void V(sem_t& sem)
    {
        int n = sem_post(&sem); assert(n == 0);
        (void)n;
    }

private:
    std::vector<T>      _v;             // 环形队列容器
    size_t              _capacity;      // 环形队列容量
    int                 _pSubscript;    // 生产力位置下标
    int                 _cSubscript;    // 消费者位置下标
    sem_t               _pSem;          // 生产者资源
    sem_t               _cSem;          // 消费者资源
    pthread_mutex_t     _pMutex;        // 生产者互斥锁
    pthread_mutex_t     _cMutex;        // 消费者互斥锁
};

【ThreadTask.hpp】

md-end-block 复制代码
#pragma once
#include <cstdio>
#include <iostream>
#include <string>
#include <functional>
#include "ThreadRingQueue.hpp"
class TaskCalculate;
class TaskSave;

template<class C, class S>
class ThreadRingQueues
{
public:
    ThreadRingQueue<C>* _cTask;
    ThreadRingQueue<S>* _sTask;
};

class TaskCalculate
{
private:
    // 定义仿函数
    using func_t = std::function<int(const int, const int, const char)>;

public:
    /* - 无参构造函数 
     */
    TaskCalculate()
    {}

    /* - 带参数的构造函数
     */
    TaskCalculate(func_t func, const int x, const int y, const char op)
        : _func(func)
        , _x(x)
        , _y(y)
        , _op(op)
    {}

public:
    /* - ()运算符重载
     */
    std::string operator()()
    {
        int result = _func(_x, _y, _op);

        char buffer[64];
        snprintf(buffer, sizeof(buffer), "%d %c %d = %d", _x, _op, _y, result);
        return buffer;
    }

public:
    std::string TaskString()
    {
        char buffer[64];
        snprintf(buffer, sizeof(buffer), "%d %c %d = ?", _x, _op, _y);
        return buffer;
    }

private:
    int     _x;    // 第一个计算值
    int     _y;    // 第二个计算值
    char    _op;   // 第三个计算值
    func_t  _func; // 仿函数类型
};


int Calculate(const int x, const int y, const char op)
{
    int calRet = 0;

    switch(op)
    {
        case '+':
        {
            calRet = x + y;
            break;
        }
        case '-':
        {
            calRet = x - y;
            break;
        }
        case '*':
        {
            calRet = x * y;
            break;
        }
        case '/':
        {
            if (y == 0)
            {
                std::cerr << "div zero error!" << std::endl;
                calRet = -1;
            }
            else
            {
                calRet = x / y;
            }

            break;
        }
        case '%':
        {
            if (y == 0)
            {
                std::cerr << "mod zero error!" << std::endl;
                calRet = -1;
            }
            else
            {
                calRet = x % y;
            }

            break;            
        }
        default:
        {
             break;
        }
    }

    return calRet;
};



class TaskSave
{
private:
    using func_t = std::function<void(const std::string&)>;

public:
    /* - 无参构造函数 
     */
    TaskSave() 
    {}

    /* - 带参构造函数 
     */
    TaskSave(func_t func, const std::string& msg) 
        : _func(func)
        , _msg(msg)
    {}

public:
    /* - ()运算符重载
     */
    void operator()()
    {
        _func(_msg);
    }

private:
    std::string _msg;   
    func_t      _func;
};

void FileSave(const std::string& msg)
{
    // 创建打开文件目录
    std::string target = "./Log.txt";

    // 打开文件
    FILE* fpath = fopen(target.c_str(), "a+");
    if(fpath == nullptr)
    {
        std::cerr << "fopen fail!" << std::endl;
        return;
    }

    // 写入文件
    fputs(msg.c_str(), fpath);
    fputs("\n", fpath);

    // 关闭文件
    fclose(fpath);
}
相关推荐
Soneswy31 分钟前
Linux系统篇--重定向与文件操作命令
linux
有毒的教程35 分钟前
Linux底层存储路由配
linux·运维·网络
Linux系统运维工程师_郭主任2 小时前
运维人还在被拒offer?现在能考的,只有 RHCE 10.0。
linux
自养Agent2 小时前
MCP Server Boundary Audit Report
linux·服务器·人工智能·windows·大模型·llm
Coisinier3 小时前
docker命令管理镜像及容器
linux·运维·nginx·docker·容器·操作系统
A心有千千结3 小时前
通过 SSH 反向代理让服务器借用本机网络访问 GitHub 的最佳实践
linux·服务器·网络·git·ssh·github
书生执笔画浮沉4 小时前
Linux 内核模块为什么不能自己解决兼容性问题?
linux·c·kernel·update·灵感
15Moonlight4 小时前
Linux网络(03):Socket 编程 TCP
linux·网络·tcp/ip
广州灵眸科技有限公司4 小时前
单芯片同时跑 Linux 与 RT-Thread:RV1126B的AMP异构方案完整拆解
linux·运维·服务器
小雪崩4 小时前
嵌入式学习:Linux驱动开发总结
linux·驱动开发·学习