C++武艺修炼--基于C++17实现后台异步执行多任务
该功能是基于C++17实现后台异步执行多任务。
在实际开发中,经常会遇到开启多个异步线程,去执行不同的任务。比如我有一个需求,每20秒去读取某个文件,每50秒去查询数据库某个字段,每30分钟去删除某个数据库或文件。那么最直观的思路就是,开启三个异步线程,分别去执行各自的任务,我也问过很多人,他们也都是这么做的。有一个有开发十多年的C++工程师,也是这么说的,他说现在的CPU性能这么高,压根不需要考虑开多个线程影响性能的问题。
我听完后,陷入了沉思。我沉思的原因不是思考他说的话是对是错。而是思考,是不是工作后,大家对开发的执着度及热情都极度低靡。感觉完成任务完成需求就行,至于怎么完成,完全不重要。而且领导也不懂,压根不关注怎么完成的,反而那些执着于性能和简洁性的人,会被领导觉得不行,代码量少,哈哈。你和他说前期封装轮子可能需要点时间,后期需求省时间。领导说:“我不管你怎么实现,我只要结果,我明天就要看到第一个完整的功能,后天看到第二个,大后天看到第三个”。
如果你按照自己的想法,造轮子就得两天,最后一天把三个任务都放入轮子中,也是三天正好把任务干完。但是前两天可得煎熬了。领导说:“你这两天的任务怎么没完成,没完成怎么也不加班!!!你这个人工作态度严重不行,太差劲了!!!” 于是你和领导解释,说是第三天肯定能把任务都写完。领导说:“现在不是第三天交任务的问题,而是你最近两天没完成任务,也不加班,是你的工作态度的问题!!!你的工作态度出现严重问题,我现在怀疑你到底能不能胜任这份工作!!!你写一份检讨报告,明天在单位读一下,引以为戒!!!”
于是乎,有一些人,按照领导的要求,每天写一个需求,写完了给领导看,领导乐呵呵,说:“你这个人,能力强,工作态度好,尽心尽责,年底的优秀员工就是你啦!!!” 听到领导的认可,你也乐呵呵,你还可能会升职涨薪。那这时的你还专注性能吗?还专注代码的简洁性,低耦合性吗?还会专注写出高质量代码吗?
反正我还是坚持做自己。引用一下罗永浩的金句 “彪悍的人生,不需要解释!”
好啦,开始分析这个需求。有三个不同任务需求,分别需要不同时间循环触发,首先需要确定的是,这三个任务本身不是死循环任务,如果任务本生是死循环的话,那么它自生就得单独开一个异步线程去执行。
那么可不可以把这三个任务放入一个异步线程呢?让这一个线程去循环执行不同的任务呢。思路确定了,那么就开始设计实现步骤啦。
这三个任务可以放入容器中,在线程中依次遍历,并执行。
1.但是我还想让线程循环时多睡觉,比如任务执行分别是,5s,10s,13s。
那么我想每此循环睡5s,再分别检测执行。后来发现实现起来有点难,读者有没有大佬,欢迎给出好的实现思路。
2.第二种方法是找5s,10s,13s的最小公因数,以此为基准,进行循环遍历。
3.第三种就是索性每秒循环遍历一次,谁的时间到了,就执行谁。
第一种方法我写了很久,反正没写出来,急需一个算法大佬帮忙优化。
第二种方法直接弃用了,感觉画蛇添足,没啥意思。
第三种方法最直接,也最没啥挑战性,但是迫不得已,还是选用这种方法啦。
其实不管用哪种方法,整体实现思路都一样,只不过是最后循环遍历实现的细节不一样而已。那么就一起来捋一下思路。
1.需要有一个任务管理类进行记录要实现的功能和函数。
2.需要一个管理类进行存储任务,并实现其所有任务添加、开始和结束等。
3.需要一个单例管理,方便多个不同地方的任务添加。
4.进行调用测试。
以下代码是根据第三种方来进行实现:
1.实现任务管理类进行记录要实现的功能和函数。
2.实现管理类进行存储任务,并实现其所有任务添加、开始和结束等。
#ifndef FUNMANAGE_H #define FUNMANAGE_H #include #include #include #include #include #include #include #include #include #include #include #include #include #include struct Task { int iID = 0; Task(int iINID, std::function pfun) : iID(iINID) { func = std::make_shared(std::move(pfun)); isUsing = std::make_shared(); } Task() = delete; public: void runFuncByCounter(const int iNum) noexcept { if((++m_iCounter) >= iNum) { runFunc(); m_iCounter = 0; } } void runFunc() noexcept { dowithAtomic(*func); } public: bool dowithAtomic(std::function func) noexcept { while(isUsing->load(std::memory_order_acquire)) std::this_thread::yield(); isUsing->store(true,std::memory_order_release); if(func) func(); isUsing->store(false,std::memory_order_release); return true; } private: std::shared_ptr func; std::shared_ptr isUsing; int m_iCounter = 0; }; struct FunManageBase { int generateTimeBasedID(){ auto now = std::chrono::system_clock::now(); std::time_t now_time = std::chrono::system_clock::to_time_t(now); return static_cast(now_time); } int gcd(int a, int b) { while (b != 0) { int temp = b; b = a % b; a = temp; } return a; } int gcd_multiple(const std::vector& numbers) { if (numbers.empty()) return 0; // 如果数组为空,返回0 int result = numbers[0]; for (size_t i = 1; i first); ++iter; } return retVec; } }; struct FunManage : FunManageBase { //这里 应该返回一个IP, 让用户根据IP自己管理 template int pushFun(int iSecTime, F&& f, Args&&... args) noexcept(noexcept(std::forward(f)(std::forward(args)...))) { auto task = [f = std::forward(f), args_tuple = std::make_tuple(std::forward(args)...)]() mutable { return std::apply(f, args_tuple); }; std::unique_lock lock(queue_mutex); const int iRetID = generateTimeBasedID(); tasks.emplace(iSecTime, Task{iRetID, std::move(task)}); return iRetID; } //开始全部任务 void startAll()noexcept { if(isStart) return; std::thread threadWeak([this]{ auto iter = tasks.begin(); int iBegTime = 0; if(iter != tasks.end()) iBegTime = iter->first; while(isStart) { std::this_thread::sleep_for(std::chrono::seconds(1)); while(iter != tasks.end()) { iter->second.runFuncByCounter(iter->first); ++iter; } iter = tasks.begin(); } }); isStart = true; threadWeak.detach(); } //结束全部任务 void stopAll()noexcept { isStart = false; } //移除任务 通过ID bool removeTaskByID(const int iID)noexcept { auto iter = tasks.begin(); while(iter != tasks.end()) { if(iter->second.iID == iID) { if(iter->second.dowithAtomic({})) { tasks.erase(iter); return true; } } ++iter; } return false; } ///后期大家想添加接口随便添加就行 ///比如,通过ID,单独开启/关闭 某一个或者某几个任务。 ///这里俺就不写啦,比较简单,也不难。 private: std::multimap tasks; std::mutex queue_mutex; bool isStart = false; }; #endif // FUNMANAGE_H
以下实现的是:
3.实现一个单例管理,方便多个不同地方的任务添加。
有关该类的.h头文件的实现:
#ifndef TIMERFUNMANAGE_H #define TIMERFUNMANAGE_H 写一个开启独立线程 ///通过定时器 ///处理多个回调函数 ///实现在FunManage.h文件 #include "FunManage.h" class TimerFunManage { public: static TimerFunManage *getInstance(); FunManage& getFunManage(); private: TimerFunManage(); ~TimerFunManage(); private: static TimerFunManage* TimerFun_instance; FunManage m_FunManage; }; #endif // TIMERFUNMANAGE_H
有关该类的.cpp源文件的实现:
#include "TimerFunManage.h" TimerFunManage* TimerFunManage::TimerFun_instance{}; TimerFunManage* TimerFunManage::getInstance() { if(!TimerFun_instance) TimerFun_instance = new TimerFunManage(); return TimerFun_instance; } FunManage &TimerFunManage::getFunManage() { return m_FunManage; } TimerFunManage::TimerFunManage() { } TimerFunManage::~TimerFunManage() { m_FunManage.stopAll(); }
下面进行第四步调用测试:
std::string s_str = "kkkkkkkk"; int main() { auto& pManage = TimerFunManage::getInstance()->getFunManage(); int iiii = 99999; auto lamda_AAA = [iiii]() { std::cout std::cout std::cout