当前位置: 首页 > news >正文

线程池项目代码细节2

自定义线程池使用实例。

/*
example:
ThreadPool pool;
pool.start(4);class MyTask : public Task
{public:void run() { // 线程代码... }
};pool.submitTask(std::make_shared<MyTask>());
*/

自定义一个Any类

创建一个 Any 类,它可以安全地存储和检索任何类型的值,同时保持类型安全。它通过模板、继承和多态的组合来实现这一目标。

// Any类型:可以接收任意数据的类型
class Any
{
public:Any() = default;~Any() = default;Any(const Any&) = delete;Any& operator=(const Any&) = delete;Any(Any&&) = default;Any& operator=(Any&&) = default;// 这个构造函数可以让Any类型接收任意其它的数据template<typename T>  // T:int    Derive<int>Any(T data) : base_(std::make_unique<Derive<T>>(data)){}// 这个方法能把Any对象里面存储的data数据提取出来template<typename T>T cast_(){// 我们怎么从base_找到它所指向的Derive对象,从它里面取出data成员变量// 基类指针 =》 派生类指针   RTTIDerive<T>* pd = dynamic_cast<Derive<T>*>(base_.get());if (pd == nullptr){throw "type is unmatch!";}return pd->data_;}
private:// 基类类型class Base{public:virtual ~Base() = default;};// 派生类类型template<typename T>class Derive : public Base{public:Derive(T data) : data_(data) {}T data_;  // 保存了任意的其它类型};private:// 定义一个基类的指针std::unique_ptr<Base> base_;
};

实现方法,在Any类中创建一个基类,并创建一个成员函数指针指向基类,然后创建基类的派生类,在派生类中用模板初始化成员变量,Ang初始化时,通过模板初始化对象,并将data通过基类指针给到派生类对象,如果想去出该data的值则通过将基类指针转换为派生类指针,同时通过模板确定参数,返回派生类中的成员变量值,总结:如果想用基类指针指向派生类对象,并用基类指针调用派生类成员函数,必须将基类指针转换为派生类指针。

自定义实现信号量

// 实现一个信号量类
class Semaphore
{
public:Semaphore(int limit = 0) :resLimit_(limit){}~Semaphore() = default;// 获取一个信号量资源void wait(){std::unique_lock<std::mutex> lock(mtx_);// 等待信号量有资源,没有资源的话,会阻塞当前线程cond_.wait(lock, [&]()->bool {return resLimit_ > 0; });resLimit_--;}// 增加一个信号量资源void post(){std::unique_lock<std::mutex> lock(mtx_);resLimit_++;// linux下condition_variable的析构函数什么也没做// 导致这里状态已经失效,无故阻塞cond_.notify_all();  // 等待状态,释放mutex锁 通知条件变量wait的地方,可以起来干活了}
private:int resLimit_;std::mutex mtx_;std::condition_variable cond_;
};

Tesk类和Result类实现


// Task类型的前置声明
class Task;// 实现接收提交到线程池的task任务执行完成后的返回值类型Result
class Result
{
public:Result(std::shared_ptr<Task> task, bool isValid = true);~Result() = default;// 问题一:setVal方法,获取任务执行完的返回值的void setVal(Any any);// 问题二:get方法,用户调用这个方法获取task的返回值Any get();
private:Any any_; // 存储任务的返回值Semaphore sem_; // 线程通信信号量std::shared_ptr<Task> task_; //指向对应获取返回值的任务对象 std::atomic_bool isValid_; // 返回值是否有效
};// 任务抽象基类
class Task
{
public:Task();~Task() = default;void exec();void setResult(Result* res);// 用户可以自定义任意任务类型,从Task  继承,重写run方法,实现自定义任务处理virtual Any run() = 0;private:Result* result_; // Result对象的声明周期 》 Task的
};
/////////////////  Task方法实现
Task::Task(): result_(nullptr)
{}void Task::exec()
{if (result_ != nullptr){result_->setVal(run()); // 这里发生多态调用}
}void Task::setResult(Result* res)
{result_ = res;
}/////////////////   Result方法的实现
Result::Result(std::shared_ptr<Task> task, bool isValid): isValid_(isValid), task_(task)
{task_->setResult(this);
}Any Result::get() // 用户调用的
{if (!isValid_){return "";}sem_.wait(); // task任务如果没有执行完,这里会阻塞用户的线程return std::move(any_);
}void Result::setVal(Any any)  // 谁调用的呢???
{// 存储task的返回值this->any_ = std::move(any);sem_.post(); // 已经获取的任务的返回值,增加信号量资源
}

1. 创建对象阶段

用户代码:

cpp

auto task = std::make_shared<MyTask>();      // 创建任务
auto result = std::make_shared<Result>(task); // 创建结果对象,传入task

2. 关键:Result 构造函数

cpp

Result::Result(std::shared_ptr<Task> task, bool isValid): isValid_(isValid), task_(task)           // 1. Result 持有 Task 的共享指针
{task_->setResult(this); // 2. 让 Task 持有 Result 的原始指针
}

这一步建立了双向关联

当任务执行完

void Task::exec()
{
if (result_ != nullptr)
{
result_->setVal(run()); // 这里发生多态调用
}
}

就将执行完的返回值通过result的成员函数设置给成员变量

    Any any_; // 存储任务的返回值

    void setVal(Any any);

    // 问题二:get方法,用户调用这个方法获取task的返回值
Any get();

最后通过调用get方法得到已经存储返回值的成员变量,这个变量是一个对象,调用any_对象的

uLong sum1 = res1.get().cast_<uLong>();  // get返回了一个Any类型,得到返回值

Any any_; // 存储任务的返回值

具体cast_

	template<typename T>T cast_(){// 我们怎么从base_找到它所指向的Derive对象,从它里面取出data成员变量// 基类指针 =》 派生类指针   RTTIDerive<T>* pd = dynamic_cast<Derive<T>*>(base_.get());if (pd == nullptr){throw "type is unmatch!";}return pd->data_;}

通过模板确定存储值类型,将基类指针转换为存储该返回值类型的子类指针,然后调用data_.

http://www.xdnf.cn/news/1403929.html

相关文章:

  • 【树形数据结构】李超线段树 (Li-Chao Tree)
  • vscode新建终端默认不是cmd问题
  • sunset: sunrise
  • CCS自定义函数.h与.c问题解决办法
  • “HEU-AUTO”无线上网使用指南
  • 【多项式】快速沃尔什变换 (FWT)
  • SpringCloud Alibaba微服务--Sentinel的使用
  • 【计算机视觉】Pixel逐像素分类Mask掩码分类理解摘要
  • 2025年- H102-Lc210--3658.奇数和与偶数和的最大公约数(gcd最大公约数)--Java版
  • 【Linux基础】深入理解Linux环境下的BIOS机制
  • PostgreSQL:突破关系型数据库的边界
  • AI公司是怎样对权重和损失函数做处理的?
  • nacos3端口漂移问题
  • mysql的内置函数
  • 论《运动战》
  • 个性化导航新体验:cpolar让Dashy支持语音控制
  • Tomcat 企业级运维实战系列(四):Tomcat 企业级监控
  • 数值分析——数据误差对函数值的影响
  • nacos 2.5.1 心跳源码解析
  • 基于单片机商用电子计价秤电子秤系统Proteus仿真(含全部资料)
  • 图解LLM(AI大模型)的工作原理
  • Redis 测试:过期 key 内存释放情况
  • 深入理解shared_ptr与循环引用问题
  • node.js ---文件读写(FS模块)
  • 用【Coze】实现文案提取+创作
  • 蓓韵安禧活性叶酸独立包装日期标注
  • 加密软件哪个好用?加密软件-为数据共享提供安全保障
  • 【基础-单选】例如现在要实现一个广告弹窗,包含图片和文本等信息,使用下面那种弹窗可以实现
  • ROS 2 机器人开发$2
  • 项目管理方法论有哪些流派