【后端开发】手写一个简单的线程池

半同步半异步线程池

半同步半异步线程池分为三层:

  • 同步服务层 —— 处理来自上层的任务请求,将它们加入到排队层中等待处理。

  • 同步排队层 —— 实际上是一个“同步队列”,允许多线程添加/取出任务,并保证线程安全。

  • 异步服务层 —— 从排队层中取出任务,多线程并发处理排队层中的任务。

在这里插入图片描述

不想码字,想看的凑活着看吧!

首先,我们来实现一个 同步队列 的模板:

#pragma once#include<iostream>
#include<thread>
#include<mutex>
#include<list>template <typename T>
class Sync_Queue
{
public:Sync_Queue(int size) : max_size(size), _stop(false){}void push(T&& x)    // 添加任务{std::unique_lock<std::mutex> lock(_mutex);_notFull.wait(lock, [this] { return NotFull() || _stop; });  // 若满足其中任一条件,则继续执行if (_stop)return;_queue.push_back(std::forward<T>(x));_notEmpty.notify_one();}void pop(std::list<T>& list)    // 取出任务{std::unique_lock<std::mutex> lock(_mutex);_notEmpty.wait(lock, [this] { return NotEmpty() || _stop; });if (_stop)return;list = std::move(_queue);_notFull.notify_one();}void stop()    // 停止队列{{std::lock_guard<std::mutex> lock(_mutex);    // 先锁住, 再将 _stop 标志设置为 true_stop = true;}_notFull.notify_all();   // 在 lock_guard 外面 notify, 被唤醒的线程不需要等待 lock_guard 释放锁 _notEmpty.notify_all();}bool Empty(){std::lock_guard<std::mutex> lock(_mutex);return _queue.empty();}bool Full(){std::lock_guard<std::mutex> lock(_mutex);return _queue.size() == max_size;}size_t size(){std::lock_guard<std::mutex> lock(_mutex);return _queue.size();}private:bool NotFull() const{bool notfull = _queue.size() < max_size;if (!notfull) std::cout << "Sync_Queue is full, waiting..." << std::endl;return notfull;}bool NotEmpty() const{bool notempty = !_queue.empty();if (!notempty) std::cout << "Sync_Queue is empty, waiting..." << std::endl;return notempty;}private:std::list<T> _queue;std::mutex _mutex;std::condition_variable _notEmpty;    // 非空的条件变量std::condition_variable _notFull;     // 未满的条件变量int max_size;bool _stop;
};

现在,我们再来实现 线程池

// ThreadPool.h
#pragma once#include "Sync_Queue.h"
#include <atomic>
#include <memory>
#include <functional>using Task = std::function<void()>;  // 任务类型为一个 “可调用对象”const int MaxTaskCount = 100;class ThreadPool {
public:ThreadPool(int thread_num = std::thread::hardware_concurrency())  // 默认创建 CPU 核数的线程: _queue(MaxTaskCount), thread_stop(false){start(thread_num);}~ThreadPool(){stop();}void stop(){std::call_once(_flag, [this] { StopThreadPool(); });  // 确保多线程下只调用一次}void add_task(Task&& task)      // 添加任务{_queue.push(std::forward<Task>(task));}private:void start(int thread_num)     // 创建 thread_num 数量的线程{for (int i = 0; i < thread_num; ++i){thread_group.push_back(std::make_shared<std::thread>(&ThreadPool::RunInThread, this));}}void RunInThread(){while (!thread_stop) {std::list<Task> list;_queue.pop(list);      // 取任务; 若消息队列为空,则阻塞for (auto& task : list){if (thread_stop)return;task();     // 执行任务}}}void StopThreadPool(){_queue.stop();thread_stop = true;for (auto thread : thread_group) {if (thread->joinable())thread->join();}thread_group.clear();}private:Sync_Queue<Task> _queue;   // 同步队列std::list<std::shared_ptr<std::thread>> thread_group;   // 线程组std::atomic_bool thread_stop;std::once_flag _flag;
};

测试代码:

#include "ThreadPool.h"
#include <chrono>void test()
{ThreadPool pool(3);std::thread t1([&pool] {for (int i = 0; i < 10; ++i){auto id = std::this_thread::get_id();pool.add_task(std::move([id] {std::cout << "thread id is " << id << std::endl;}));}});std::this_thread::sleep_for(std::chrono::seconds(2));getchar();t1.join();
}int main()
{test();return 0;
}

输出如下:
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/129068.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

烂大街的测试左移和右移!

01、测试左移与右移的定义 通俗的讲&#xff1a;左移是往开发阶段移&#xff0c;右移是往发布之后移。 正常测试&#xff1a;提测后的测试工作——到——发布验证完成阶段。 测试左移&#xff1a;提测之前的测试。 如&#xff1a;代码单元测试&#xff0c;代码质量检测&…

Nacos报错Connection refused (Connection refused)(最后原因醉了,非常醉)

目录 一、问题产生二、排查思路1.nacos拒绝连接&#xff0c;排查思路&#xff1a;2.Nacos启动成功但是拒绝连接的几种原因&#xff1a; 三、实操过程&#xff08;着急解决问题直接看这个&#xff09;1.启动Nacos2.查看Nacos启动日志3.根据日志处理问题4.修改Nacos5.重启Nacos 一…

c++qt学习对象树

1.当创建的对象在堆区时候&#xff0c;如果指定的父亲是QObject派生下来的类或者QObject子类派生下来的类&#xff0c;可以不用管理释放的操作&#xff0c;将对象会放在对象树中。 2.一定程度上简化了内存回收机制 构造顺序与析构顺序相反

SpringBoot整合Activiti7——全局监听器(八)

文章目录 一、全局监听器事件类型配置方式(选)日志监听器代码实现xml文件创建全局监听器全局配置类测试流程部署流程启动流程 一、全局监听器 它是引擎范围的事件监听器&#xff0c;可以捕获所有的Activiti事件。 事件类型 ActivitiEventType 枚举类中包含全部事件类型 配置方…

跳跳狗小游戏

欢迎来到程序小院 跳跳狗 玩法&#xff1a;一直弹跳的狗狗&#xff0c;鼠标点击屏幕左右方向键进行弹跳&#xff0c;弹到不同物品会有不同的分数减扣&#xff0c;规定的时间3分钟内完成狗狗弹跳&#xff0c;快去跳跳狗吧^^。开始游戏https://www.ormcc.com/play/gameStart/198…

MySQL用户管理和授权

目录 一.用户管理 1.1.新建用户 1.2.查看用户 1.3.重命名用户rename 1.4.删除用户 1.5.修改当前登录用户密码 1.6.修改其他用户密码 1.7.忘记root 密码并找回 二.数据库用户授权 2.1.all privilege包含的权限 2.2.授予权限 ①允许指定用户查询指定数据库表 ②允许…

比较Excel中的两列目录编号是否一致

使用java代码比较excel中两列是否有包含关系&#xff0c;若有包含关系&#xff0c;核对编号是否一致。 excel数据样例如下&#xff1a; package com.itownet.hg;import org.apache.poi.xssf.usermodel.XSSFSheet; import org.apache.poi.xssf.usermodel.XSSFWorkbook;import j…

sqlsugar查询数据库下的所有表,批量修改表名字

查询数据库中的所有表 using SqlSugar;namespace 批量修改数据库表名 {internal class Program{static void Main(string[] args){SqlSugarClient sqlSugarClient new SqlSugarClient(new ConnectionConfig(){ConnectionString "Data Source(localdb)\\MSSQLLocalDB;In…

双热点机制结合。5+铜死亡+铁死亡相关基因生信思路

今天给同学们分享一篇结合铜死亡和铁死亡相关基因预测肿瘤预后、免疫和药敏的生信文章“A novel signature of combing cuproptosis- with ferroptosis-related genes for prediction of prognosis, immunologic therapy responses and drug sensitivity in hepatocellular car…

Jenkins自动化部署相关shell命令

1. 只后台启动&#xff1a; nohup java -jar jar/demo*.jar & 2. 增加命令启动日志输出位置&#xff0c;防止超时处理配置&#xff1a; nohup java -jar /soft/gitee-demo-0.0.1-SNAPSHOT.jar >mylog.log 2>&1 & 简化写法&#xff1a; nohup java -jar /s…

基于STM32F412RET6的智能桶硬件设计

一、智能桶功能需求&#xff1a; 智能桶是一直采用Cortex-M4 嵌入式平台&#xff0c;搭载NB-IotTO通讯模组、智能称重采集、智能门锁监控以及温度监测等装置。主要功能如下&#xff1a; ▲ 具有GPS定位功能&#xff0c;可以通过后台APP实时定位智能桶的位置。 ▲ 具有温度监测功…

【设计模式】第11节:结构型模式之“装饰器模式”

一、简介 装饰器模式主要解决继承关系过于复杂的问题&#xff0c;通过组合来替代继承。它主要的作用是给原始类添加增强功能。这也是判断是否该用装饰器模式的一个重要的依据。除此之外&#xff0c;装饰器模式还有一个特点&#xff0c;那就是可以对原始类嵌套使用多个装饰器。…

第5天:基础入门-资产架构amp;端口amp;应用amp;CDNamp;WAFamp;站库分离amp;负载均衡

第5天&#xff1a;基础入门-资产架构&端口&应用&CDN&WAF&站库分离&负载均衡 #知识点&#xff1a;1. 资产架构-端口&目录&插件接口&多站点&多应用 2. 番外安全-域名&服务器本身&服务厂商&管理人员 3. 考虑阻碍-站库分离&am…

Redis-使用java代码操作Redis

&#x1f3c5;我是默&#xff0c;一个在CSDN分享笔记的博主。&#x1f4da;&#x1f4da; ​ &#x1f31f;在这里&#xff0c;我要推荐给大家我的专栏《Linux》。&#x1f3af;&#x1f3af; &#x1f680;无论你是编程小白&#xff0c;还是有一定基础的程序员&#xff0c;这…

go语言 | grpc原理介绍(一)

参考 https://www.nowcoder.com/discuss/389810396381683712?sourceSSRsearch 这里是b站对应的csdn博客&#xff0c;比较详细的介绍grpc相关原理说明&#xff0c;首先是大概的一个流程图说明。 什么是 RPC &#xff1f; 远程过程调用&#xff08;RPC&#xff09;是计算机科…

[推荐]SpringBoot java实现文件/附件上传下载 服务器 数据库 拿来就用,简单实用

推荐一个思路非常简单又很实用的文件上传下载方式&#xff0c;代码十分简练&#xff0c;可以开箱即用&#xff0c;下面是使用到的一些工具类和业务代码&#xff1b; 1.文件上传实现 判断文件类型的工具类&#xff0c;一些使用到的实体类我会凡在文末&#xff0c;需要可以的自…

吴恩达《机器学习》4-1->4-5:多变量线性回归

一、引入多维特征 在多维特征中&#xff0c;我们考虑的不再是单一的特征&#xff0c;而是一组特征&#xff0c;例如房价模型中可能包括房间数、楼层等多个特征。这些特征将组成一个向量&#xff0c;表示为(&#x1d465;₁, &#x1d465;₂, . . . , &#x1d465;ₙ)&#x…

nodelist 与 HTMLCollection 的区别

原地址 https://cloud.tencent.com/developer/article/2013289 节点与元素 根据 W3C 的 HTML DOM 标准&#xff0c;HTML 文档中的所有内容都是节点&#xff1a; 整个文档是一个文档节点每个 HTML 元素是元素节点HTML 元素内的文本是文本节点每个 HTML 属性是属性节点注释是注…

ts 简易封装 axios,统一 API

文章目录 为什么要封装目标文件结构封装通用请求方法获得类型提示http 方法文件上传使用示例实例化post 请求类型提示文件上传 总结完整代码&#xff1a; 为什么要封装 axios 本身已经很好用了&#xff0c;看似多次一举的封装则是为了让 axios 与项目解耦。比如想要将网络请求…

视频增强和修复工具 Topaz Video AI mac中文版功能

Topaz Video AI mac是一款使用人工智能技术对视频进行增强和修复的软件。它可以自动降噪、去除锐化、减少压缩失真、提高清晰度等等。Topaz Video AI可以处理各种类型的视频&#xff0c;包括低分辨率视频、老旧影片、手机录制的视频等。 使用Topaz Video AI非常简单&#xff0c…