当前位置: 首页 > 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.dtcms.com/a/359239.html

相关文章:

  • 互联网医院系统源码解析:如何从零搭建高效的在线问诊平台
  • SNMPv3开发--EngineID安全访问机制
  • 腾讯云的运维笔记——从yum的安装与更新源开始
  • 深入理解 Linux 驱动中的 file_operations:从 C 语言函数指针到类比 C++ 虚函数表
  • centos7中MySQL 5.7.32 到 5.7.44 升级指南:基于官方二进制包的原地替换式升级
  • 有个需求:切换车队身份实现Fragment的Tab隐藏显示(车队不显示奖赏)
  • SNMPv3开发--简单使用
  • 【Linux基础】深入理解Linux环境下的BIOS机制
  • Python - 机器学习:从 “教电脑认东西” 到 “让机器自己学规律”
  • 项目管理和产品管理的区别
  • docker,mysql安装
  • vector的学习和模拟
  • 揭秘表格推理的“思维革命”:RoT模型介绍
  • 【机器学习基础】机器学习中的容量、欠拟合与过拟合:理论基础与实践指南
  • Vue生命周期、工程化开发和脚手架、组件化开发
  • 学习日志41 python
  • 打工人日报#20250830
  • 内网后渗透攻击--跨域攻击
  • 给某个conda环境安装CUDA 12.4版本 全局CUDA不变
  • Mybatis 动态sql
  • 【树形数据结构】李超线段树 (Li-Chao Tree)
  • 【深度学习新浪潮】有没有什么方法可以将照片变成线描稿,比如日式漫画的那种?
  • 嵌入式学习日记(38)HTTP
  • Ansible主机模式与文件导入技巧
  • 开发环境全面配置指南:语言环境与数据库工具
  • 【面试场景题】订单超时自动取消功能如何设计
  • 【机器学习入门】3.3 FP树算法——高效挖掘频繁项集的“树状神器”
  • 11 C 语言 sizeof 与指针实战指南:一维 / 二维数组计算注意事项 + 笔试真题解析 + sizeof strlen 对比
  • 谈谈线程的中断退出
  • nginx(自写)