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

半同步半异步线程池

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

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

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

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

在这里插入图片描述

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

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

#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…

C语言--输出1-100以内同时能被3和5整除的数

首先我们要有1-100的数字. for(int i1;i<100;i) 如何表示同时能被3和5同时整除呢&#xff1f; 如果这个数i&#xff0c;i%30&&i%50,那么这个数就可以同时被3和5整除 if(i%30&&i%50) 最后输出即可 完整代码&#xff1a; #include<stdio.h> void Sh…

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…

linux shell操作- 03 用户切换及shell案例

文章目录 用户切换shell 脚本案例 用户切换 linux是多用户的、多进程的操作系统&#xff0c;通常多个用户之间需要来回切换&#xff1b;su&#xff0c;switch user 命令 参数 - &#xff0c; 切换用户&#xff0c;同时切换工作目录&#xff08;为目标用户的家目录&#xff09;…

双热点机制结合。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…

生产环境评估机器的方法

学习了kafka的原理知识后&#xff0c;还要学会如何评估生产环境集群&#xff0c;如果是一个大数据架构师&#xff0c;这个是必须要会的&#xff0c;比如kafka集群、Hbase集群、hadoop集群&#xff0c;评估集群的方式差不多&#xff0c;现在以kafka为例。 场景 电商平台&#x…

基于STM32F412RET6的智能桶硬件设计

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

模仿看门狗写个简单的分布式锁demo

public void tryLock() { // 判断key是否存在,存在则返回 用户信息 // 不存在则生成key,将信息(用户名&#xff0c;用户id,线程名)放入TYuanGong pcCurrentUser TisUtils.getPcCurrentUser();String name Thread.currentThread().getName();//// redis redis.c…

14、SpringCloud -- WebSocket 实时通知用户

目录 实时通知用户需求:代码:前端:后端:WebSocket创建 websocket-server 服务添加依赖:配置 yml 和 启动类:前端:后端代码:注意:测试:总结:实时通知用户 需求: 用户订单秒杀成功之后,对用户进行秒杀成功通知。 弹出个提示框来提示。 代码: 前端:

【设计模式】第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;是计算机科…