该功能是基于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 <vector>
#include <queue>
#include <memory>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <future>
#include <functional>
#include <stdexcept>
#include <iostream>
#include <chrono>
#include <ctime>
#include <map>
#include <stdexcept>
struct Task {
int iID = 0;
Task(int iINID, std::function<void()> pfun) :
iID(iINID)
{
func = std::make_shared<std::function<void()>>(std::move(pfun));
isUsing = std::make_shared<std::atomic<bool>>();
}
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<void()> 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<std::function<void()>> func;
std::shared_ptr<std::atomic<bool>> 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<int>(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<int>& numbers)
{
if (numbers.empty()) return 0; // 如果数组为空,返回0
int result = numbers[0];
for (size_t i = 1; i < numbers.size(); ++i) {
result = gcd(result, numbers[i]);
if (result == 1) break; // 如果GCD已经是1,可以提前结束
}
return result;
}
std::vector<int> getmultimapKeys(const std::multimap<int, Task>& tasks)
{
std::vector<int> retVec;
auto iter = tasks.begin();
while(iter != tasks.end())
{
retVec.push_back(iter->first);
++iter;
}
return retVec;
}
};
struct FunManage : FunManageBase
{
//这里 应该返回一个IP, 让用户根据IP自己管理
template<class F, class... Args>
int pushFun(int iSecTime, F&& f, Args&&... args) noexcept(noexcept(std::forward<F>(f)(std::forward<Args>(args)...)))
{
auto task = [f = std::forward<F>(f), args_tuple = std::make_tuple(std::forward<Args>(args)...)]() mutable {
return std::apply(f, args_tuple);
};
std::unique_lock<std::mutex> 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<int, Task> 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 << "--------tiaoshi888::::lamda_AAA:::::" << iiii << std::endl;
};
auto lamda_BBB = []() {
std::cout << "--------tiaoshi888::::lamda_BBB:::::" << "---------------" << std::endl;
};
auto lamda_CCC = [](std::string& str) {
std::cout << "--------tiaoshi888::::lamda_CCC:::::" << str << std::endl;
};
pManage.pushFun(8, lamda_AAA);
pManage.pushFun(5, lamda_CCC, s_str);
pManage.pushFun(1, lamda_BBB);
pManage.startAll();
return 0;
}
下面看打印输出:
是不是完全没问题呀。好啦今天的代码到此为止。后续俺还会不断更新新的博客,而且俺可是对代码把控比较严格的,也是尽百分比的努力不做水文,压根没有做水文的习惯,宁缺勿滥。大家想关注的话,可以关注一下,忍着多难受呀,是吧。
还要告诉大家一个小秘密,俺之前写的博客,平台要默认提升成会员权限。坚决不能让这样的事情发生,代码就是用来开源的,用来分享的,这不是我用来赚钱的工具,也不希望成为某PT掠夺知识的,剥夺他人学习的权益。如果某天俺的文章需要用VIP才能看,那俺就决定退出啦,不玩啦。
------------------------------------------------------------------------------------------------------------
下面还是分享毫无意义的文字啦,简直是无病呻吟。
该文字写于2025.02.25。不喜欢写标题。
赤色的风,越过荒芜和贫瘠
原野上的栅栏 困住固步自封的奴隶
卷携而来的尘土 飞扬的孤独与彳亍
那昨夜的一席春梦 如梦幻泡影
下榻的山谷是沟壑的崛起
高山上的漫天星河 是幻想的假想
凭空幻之力 抵月照沟渠
黑色的夜凭空捏造现实 却再一次选择消失
祝福大家每天开心快乐!!!