[c++]在leetcode中使用线程池加速做题

我随随便便写了个O(N三次方)的代码,但是超时了,怎么办?
一定是平台的问题,嘻嘻嘻🤭🤭🤭🤭
我用上线程池,多开运算,就不会超时啦。
leecode支持C++11以上的语法,正好可以用STL搞多线程。
但是线程开多了也是麻烦事,这里给个线程池的实现。经过测试可以在leecode编译哦。

#define dpool_THREADPOOL_H

#ifdef _WIN32
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
#endif // win32
/*
* @file dpool线程池
* @brief 用c++11写的跨平台线程池,支持c++14 c++17语法标准
*       - 使用简单,不易出错。
        - 支持线程复用,提升性能。
        - 支持懒惰创建线程。
        - 必要时自动回收空闲的线程。
* @author senlinzhan  | fgfxf
* @date 2017  | 2023改进
* @note 用chartGPT辅助下增加了对c++17 20版本的支持,增加了一些get set函数
* @note 线程池会自动回收空闲线程,无需从外部主动释放,请放心上船
* @warning 作为合格的开发者,应当保证线程池单例化,因此函数和声明没有分离,
* 如果想要多实例的线程池,请把本hpp文件中的“非模板类成员函数”分离出来即可。
* @warning set方法没有做健壮性检测,作为合格的开发者,相信您不会犯这样的错误。
*
* @see https://github.com/senlinzhan/dpool
*/

#include <condition_variable>
#include <functional>
#include <future>
#include <memory>
#include <mutex>
#include <queue>
#include <thread>
#include <unordered_map>

namespace dpool {

class ThreadPool {
public:
    using MutexGuard = std::lock_guard<std::mutex>;
    using UniqueLock = std::unique_lock<std::mutex>;
    using Thread = std::thread;
    using ThreadID = std::thread::id;
    using Task = std::function<void()>;
    /*
     * @brief dpool初始化,自动获取数量
     * @param 最大线程数自动获取硬件数量
     */
    ThreadPool()
        : ThreadPool(Thread::hardware_concurrency()) // 自动获取cpu硬件线程数
    {}
    /*
     * @brief dpool初始化,指定最大线程数
     * @param 最大线程数
     */
    explicit ThreadPool(size_t maxThreads)
        : quit_(false), currentThreads_(0), idleThreads_(0),
          maxThreads_(maxThreads), WAIT_MINUTES(10), minThreads_(2) {
        if (maxThreads < minThreads_)
            maxThreads = minThreads_; // 最小不小于2
    }

    // disable the copy operations
    /*
     * @brief 禁止拷贝
     */
    ThreadPool(const ThreadPool&) = delete;
    /*
     * @brief 禁止拷贝
     */
    ThreadPool& operator=(const ThreadPool&) = delete;

    /*
     * @brief 析构线程池
     */
    ~ThreadPool() {
        if (!quit_)
            Stop();
    }

    void Stop() {

        {
            MutexGuard guard(mutex_);
            quit_ = true;
        }
        cv_.notify_all(); // 唤醒所有线程

        for (auto& elem : threads_) {
            assert(elem.second.joinable());
            elem.second.join(); // 逐个线程地等待退出
        }
    }

    /*
     * @brief 初始化线程池(非必要)
     *  当创建好线程池对象后,当前工作线程数量为0,调用此函数增加工作线程
     *          非必要操作,适合强迫症程序员
     */
    void Initialize() {
        MutexGuard guard(mutex_);
        if (quit_)
            return;
        while (currentThreads_ < minThreads_) {
            Thread t(&ThreadPool::worker, this);
            assert(threads_.find(t.get_id()) == threads_.end());
            threads_[t.get_id()] = std::move(t); // map线程id---线程 对应
            ++currentThreads_;
        }
    }

    /*
     * @brief dpool提交任务
     * @param func函数
     * @param ...变长参数,func的函数列表
     * @return 利用标准库函数std::future异步获取函数的返回值
     */
    template <typename Func, typename... Ts>
    auto submit(Func&& func, Ts&&... params)
    //  -> std::future<typename std::result_of<Func(Ts...)>::type>
    {
        // 函数参数转发---完美转发
        auto execute =
            std::bind(std::forward<Func>(func), std::forward<Ts>(params)...);
#if (defined(_MSVC_LANG) && _MSVC_LANG > 201402L) || __cplusplus > 201402L
        // 当使用msvc编译器,则_MSVC_LANG。否则gcc/g++ __cplusplus
        // msvc中__cplusplus始终是98年,f**k
        using ReturnType =
            std::invoke_result_t<Func, Ts...>; // 利用编译期运算获取函数返回类型
#else
        using ReturnType = typename std::result_of<Func(Ts...)>::type;
#endif

        using PackagedTask = std::packaged_task<ReturnType()>;
        // 移动execute变成task
        auto task = std::make_shared<PackagedTask>(std::move(execute));
        auto result = task->get_future();

        MutexGuard guard(mutex_);
        assert(!quit_);
        // tasks任务列表添加一个task
        tasks_.emplace([task]() { (*task)(); });
        // 当空闲线程>0,就执行一个
        if (idleThreads_ > 0) {
            cv_.notify_one(); // 唤醒一个线程
        }
        // 创建新的线程
        else if (currentThreads_ < maxThreads_) {
            Thread t(&ThreadPool::worker, this);
            assert(threads_.find(t.get_id()) == threads_.end());
            threads_[t.get_id()] = std::move(t); // map线程id---线程 对应
            ++currentThreads_;
        }

        return result;
    }

    /*
     * @brief 返回当前创建的线程的数量
     * @return size_t线程数量
     */
    size_t getCurrenThreadsNum() const { return currentThreads_; }
    /*
     * @brief 返回当前线程池最大线程数量
     * @return size_t线程数量
     */
    size_t getMaxThreadsNum() { return maxThreads_; }
    /*
     * @brief 设置最大线程数量
     * @param size_t线程数量
     */
    void setMaxThreadsNum(size_t szNewMaxThreadCount) {
        maxThreads_ = szNewMaxThreadCount;
    }

    /*
     * @brief 返回当前线程池最小线程数量
     * @return size_t线程数量
     */
    size_t getMinThreadsNum() { return minThreads_; }
    /*
     * @brief 设置最小线程数量
     * @param size_t线程数量
     */
    void setMinThreadsNum(size_t szNewMinThreadCount) {
        minThreads_ = szNewMinThreadCount;
    }

    /*
     * @brief 获取线程空闲等待销毁时长
     * @param size_t单位:分钟
     */
    size_t getWaitMinutes() { return WAIT_MINUTES; }
    /*
     * @brief 设置线程空闲销毁时长,单位:分钟
     */
    void setWaitMinutes(size_t WaitMinutes) {
        MutexGuard guard(mutex_);
        WAIT_MINUTES = WaitMinutes;
    }

    /*
     * @brief 获取当前任务队列等待分配任务数量
     * @return size_t 任务数量
     */
    size_t getTaskCount() {
        MutexGuard guard(mutex_);
        return tasks_.size();
    }

private:
    /*
     * @brief 工作线程,submit函数提交任务之后,如果没有线程就会创建线程。
     * 子线程执行这里
     */
    void worker() {
        while (!quit_) {
            Task task;
            { // 代码块 加锁
                UniqueLock uniqueLock(mutex_);
                ++idleThreads_;

                // 工作线程休眠:当超时WAIT_MINUTES或者quit__或者任务非空时唤醒。
                bool hasTimedout = !cv_.wait_for(
                    uniqueLock, std::chrono::minutes(WAIT_MINUTES),
                    [this]() { return quit_ || !tasks_.empty(); });
                --idleThreads_;
                // 如果任务列表空了
                if (tasks_.empty()) {
                    if (quit_) // 如果退出
                    {
                        --currentThreads_; // 线程会由析构函数回收,不做处理
                        return;
                    }
                    // 超时销毁线程
                    if (hasTimedout) {
                        DestroyFinishedThreads(); // 清理其他线程
                        if (currentThreads_ > minThreads_) {
                            // 如果线程比较多
                            --currentThreads_;
                            finishedThreadIDs_.emplace(
                                std::this_thread::
                                    get_id()); // 当前线程自己加入到待清理队列
                            return;
                        }
                        continue; // 继续循环
                    }
                }

                // 任务列表不为空时,执行队列函数
                task = std::move(tasks_.front());
                tasks_.pop();
                // 代码解锁
            }
            task();
        }
    }
    /*
     * @brief 清理线程
     * 清理所有加入到finishedThreadIDs_队列的线程
     */
    void DestroyFinishedThreads() {
        // 循环将所有finishedThreadIDs_队列中的线程都释放掉
        // finishedThreadIDs_是完成任务的线程id队列
        while (!finishedThreadIDs_.empty()) {
            ThreadID id = std::move(finishedThreadIDs_.front());
            finishedThreadIDs_.pop();
            auto iter = threads_.find(id);
            assert(iter != threads_.end());
            assert(iter->second.joinable());
            iter->second.join();
            threads_.erase(iter);
        }
    }

    size_t WAIT_MINUTES; // 线程等待销毁,若任务列表空,等待多少时长线程池被优化
    std::atomic<bool> quit_;     // 退出标志
    size_t currentThreads_;      // 当前线程数量
    size_t idleThreads_;         // 空闲线程数量
    size_t maxThreads_;          // 最大线程数量
    size_t minThreads_;          // 最小线程数量,保证突发性
    mutable std::mutex mutex_;   // 互斥体
    std::condition_variable cv_; // 条件变量,用于线程唤醒
    std::queue<Task> tasks_;     // 任务队列
    std::queue<ThreadID> finishedThreadIDs_;       // 完成的线程ID
    std::unordered_map<ThreadID, Thread> threads_; // map   线程id -- 线程
};

} // namespace dpool

#endif /* THREADPOOL_H */

使用案例:
leecode 437

    long long int m_target = 0;
    long long int m_numsOfPathSum = 0;

class Solution {

public:
    dpool::ThreadPool m_pool;
    Solution(){
        m_numsOfPathSum = 0;
    }
    int pathSum(TreeNode* root, int targetSum) {
        if (!root)
            return 0; // 根节点不为空
        m_target = targetSum;
        //DFS(root, 0);
        pathSum2(root,targetSum);
        while(1){
            if(m_pool.getCurrenThreadsNum()==m_pool.getIdleCount())
                break;
                std::this_thread::sleep_for(std::chrono::milliseconds(1)); // 休眠100毫秒
        }
        
        return m_numsOfPathSum;
    }
    int pathSum2(TreeNode* root, int targetSum) {
        if (!root)
            return 0; // 根节点不为空
        //DFS(root, 0);
        m_pool.submit(DFS,root,0);
        //m_pool.submit(pathSum2,root->left,targetSum);
        //m_pool.submit(pathSum2,root->right,targetSum);
        pathSum2(root->left, targetSum);
        pathSum2(root->right, targetSum);
        return m_numsOfPathSum;
    }
    // 传入参数,不带当前节点的链接和
    static void DFS(TreeNode* root,     long long int currentSum) {
        // 带上本节点的路径和
        if ((root->val + currentSum) == m_target)
            m_numsOfPathSum++;
        if (root->left) {
            DFS(root->left,
                (root->val + currentSum)); // 递归左侧,且带上父、自己
            // 递归左侧,不带父节点,从自己开始,带上自己
        }
        if (root->right) {
            DFS(root->right,
                (root->val + currentSum)); // 递归右侧,且带上父、自己
            // 递归右侧,不带父节点,带上自己,从自己开始
        }
    }
};

posted @ 2024-07-11 19:16  范哥范小飞  阅读(2)  评论(0编辑  收藏  举报