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 <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。不喜欢写标题。

赤色的风,越过荒芜和贫瘠
原野上的栅栏 困住固步自封的奴隶
卷携而来的尘土 飞扬的孤独与彳亍
那昨夜的一席春梦 如梦幻泡影
下榻的山谷是沟壑的崛起
高山上的漫天星河 是幻想的假想
凭空幻之力 抵月照沟渠
黑色的夜凭空捏造现实 却再一次选择消失

在这里插入图片描述
祝福大家每天开心快乐!!!

评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值