目录
【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】初始化信号量
#include <semaphore.h>
int sem_init(sem_t *sem, int pshared, unsigned int value);
// 参数:
// pshared:0表示线程间共享,非零表示进程间共享
// value:信号量初始值
【1.2】销毁信号量
int sem_destroy(sem_t *sem);
【1.3】等待信号量
// 功能:等待信号量,会将信号量的值减1
int sem_wait(sem_t *sem); //P()
【1.4】发布信号量
// 功能:发布信号量,表示资源使用完毕,可以归还资源了。将信号量值加1。
int sem_post(sem_t *sem);//V()
【2】基于环形队列的生产消费模型
环形队列采用数组模拟,用模运算来模拟环状特性。
环形结构起始状态和结束状态都是一样的,不好判断为空或者为满,所以可以通过加计数器或者标记位来判断满或者空。另外也可以预留一个空的位置,作为满的状态。
【2.1】生产消费模型打印数字模型
【makefile文件】
cc=g++
standard=-std=c++11ringQueue:RingQueue.cc$(cc) -o $@ $^ $(standard) -l pthread.PHONY:clean
clean:rm -rf ringQueue
【RingQueue.hpp文件】
#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) {// 生产前:申请空间信号量,保证一定可以进行生产,空间信号量-1P(_pSpaceSem);// 加锁pthread_mutex_lock(&_pMutex);_cyclicQueue[_pSubcript++] = in;_pSubcript %= _capacity;// 解锁pthread_mutex_unlock(&_pMutex);// 生产完:占用一个空间,数据信号量+1V(_cDataSem);}/* 消费任务 */void Pop(T* out){// 消费前:申请数据信号量,保证一定可以进行消费,数据信号量-1P(_cDataSem);// 加锁pthread_mutex_lock(&_pMutex);*out = _cyclicQueue[_cSubcript++];_cSubcript %= _capacity;// 解锁pthread_mutex_unlock(&_pMutex);// 消费完:少了一个空间,空间信号量+1V(_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文件】
#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 消费者:Consumerpthread_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文件】
cc=g++
standard=-std=c++11ringQueue:RingQueue.cc$(cc) -o $@ $^ $(standard) -l pthread.PHONY:clean
clean:rm -rf ringQueue
【RingQueue.hpp文件】
#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) {// 生产前:申请空间信号量,保证一定可以进行生产,空间信号量-1P(_pSpaceSem);// 加锁pthread_mutex_lock(&_pMutex);_cyclicQueue[_pSubcript++] = in;_pSubcript %= _capacity;// 解锁pthread_mutex_unlock(&_pMutex);// 生产完:占用一个空间,数据信号量+1V(_cDataSem);}/* 消费任务 */void Pop(T* out){// 消费前:申请数据信号量,保证一定可以进行消费,数据信号量-1P(_cDataSem);// 加锁pthread_mutex_lock(&_pMutex);*out = _cyclicQueue[_cSubcript++];_cSubcript %= _capacity;// 解锁pthread_mutex_unlock(&_pMutex);// 消费完:少了一个空间,空间信号量+1V(_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文件】
#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文件】
#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 消费者:Consumerpthread_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文件】
# 创建替换变量并且复制对应的含义
cc := g++
standard := -std=c++11# 创建依赖关系
myThreadRingQueue: ThreadRingQueue.cc$(cc) -o $@ $^ $(standard) -l pthread# 创建辅助命令
clean:rm -rf myThreadRingQueue .PHONY: clean
【ThreadBase.hpp】
#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; // 线程Idfunc_t _threadCallBack; // 线程回调函数void* _threadArgs; // 线程回调函数参数
};
【ThreadMutex.hpp】
#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】
#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】
#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){// 生产前:保证可以生产->空间信号量-1P(_pSem);// 加锁pthread_mutex_lock(&_pMutex);_v[_pSubscript++] = in;_pSubscript %= _capacity;// 解锁pthread_mutex_unlock(&_pMutex);// 生产完:占用一个空间->数据信号量+1V(_cSem);}/* - 消费任务*/ void Pop(T* out){// 消费前:保证可以消费->数据信号量-1P(_cSem);// 加锁pthread_mutex_lock(&_pMutex);*out = _v[_cSubscript++];_cSubscript %= _capacity;// 解锁pthread_mutex_unlock(&_pMutex);// 消费完:减少一个空间->空间信号量+1V(_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】
#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);
}