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

半同步半异步线程池

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

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

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

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

在这里插入图片描述

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

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

#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.hqwc.cn/news/161090.html

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

相关文章

Nginx服务器安装证书并启用SSL(acme.sh)

前提 您已购置vps服务器&#xff0c;例如阿里云全球站ecs、AWS EC2、Azure VM、GCP Compute等安全组已开启80、443端口&#xff0c;且访问源设置为0.0.0.0/0域名已设置A记录指向当前操作服务器&#xff0c;若您使用aws ec2&#xff0c;有公有 IPv4 DNS&#xff0c;可供使用 安…

视频视觉效果制作After Effects 2023 MacOS中文

After Effects 2023是一款业界领先的动态图形和视觉特效软件。它提供了强大的工具集&#xff0c;帮助用户创建引人入胜的视觉效果、动态图形和电影级特效。新的版本带来了更快的渲染速度、增强的图像处理和优化的工作流程&#xff0c;使用户能够更高效地工作。无论您是在电影、…

阿里云域名实战

一、准备阿里云服务器&#xff0c;实现网站功能 &#xff08;1&#xff09;百度搜索阿里云 &#xff08;2&#xff09;登录阿里云 可以使用支付宝&#xff0c;淘宝账号登录 &#xff08;3&#xff09;点击控制台 (4)创建实例&#xff0c;购买云服务器 &#xff08;5&#x…

SSH服务

目录 ​编辑 一、SSH服务基础知识 1、ssh服务介绍 2、ssh服务的配置文件路径 3、查看ssh服务的运行状态 二、SSH服务常用命令 - ssh 1、 SSH服务器 启动/关闭/重启 2、SSH远程连接语法 3、利用ssh协议传输文件和获取文件 4、SSH服务免密登陆 一、SSH服务基础知识 1、…

【Go 编程实践】从零到一:创建、测试并发布自己的 Go 库

为什么需要开发自己的 Go 库 在编程语言中&#xff0c;包&#xff08;Package&#xff09;和库&#xff08;Library&#xff09;是代码组织和复用的重要工具。在 Go 中&#xff0c;包是代码的基本组织单位&#xff0c;每个 Go 程序都由包构成。包的作用是帮助组织代码&#xf…

C#中LINQtoSQL只能在.NetFramework下使用,不能在.net 下使用

目录 一、在net7.0下无法实现LINQtoSQL 1.VS上建立数据库连接 2.VS上创建LINQtoSQL 二、在.NetFramework4.8下成功实现LINQtoSQL 1.VS上建立数据库连接 2.VS上创建LINQtoSQL 三、结论 四、理由 本文是个人观点&#xff0c;因为我百般努力在.net7.0下无法实现LINQtoSQL的…

【异常错误】Multiple top-level packages discovered in a flat-layout: [

今天在配置环境时&#xff0c;在运行代码&#xff1a; pip install -e . 出现了以下错误&#xff1a; 解决方案 在setup.py中显示加入一行 py_modules[]&#xff0c;如&#xff1a; setup(namexxxx,versionx.x,py_modules[], ) 再次运行pip指令安装即可解决。 error: Mul…

分析报告有样板了-奥威BI数据可视化报表模板

述职报告、月度数据分析报告、季度数据分析报告、区域数据分析报告……人在职场&#xff0c;数据分析报告少不了。那么&#xff0c;怎么才能在极短的时间内做出一张既好看又突出重点、分析逻辑在线的数据可视化分析报表&#xff1f;奥威BI软件的建议是采用BI数据可视化报表模板…

香港金融科技周2023:AIGC重塑金融形态

10月31日&#xff0c;由香港财经事务及库务局与投资推广署主办的“香港金融科技周2023大湾区专场”盛大启幕。中国AI决策领先企业萨摩耶云科技集团创始人、董事长兼 CEO林建明受邀参加圆桌会议&#xff0c;与中国内地、香港以及全球金融科技行业顶尖人才、创新企业、监管机构和…

20.7 OpenSSL 套接字SSL加密传输

OpenSSL 中的 SSL 加密是通过 SSL/TLS 协议来实现的。SSL/TLS 是一种安全通信协议&#xff0c;可以保障通信双方之间的通信安全性和数据完整性。在 SSL/TLS 协议中&#xff0c;加密算法是其中最核心的组成部分之一&#xff0c;SSL可以使用各类加密算法进行密钥协商&#xff0c;…

04-附注 三维空间中的线性变换

附注 三维空间中的线性变换 三维空间线性变换 这是关于3Blue1Brown "线性代数的本质"的学习笔记。 三维空间线性变换 图1 绕y轴旋转90 绕y轴旋转90后&#xff0c;各基向量所在的坐标如图1所示。用旋转后的各基向量作为矩阵的列&#xff0c;就得到变换矩阵。变换矩阵…

Redis4 渐进式遍历/自定义客户端/持久化

1.渐进式遍历 1.keys *一次性把所有的key都获取到.但是存在一个问题,一旦数据过多,redis就会被阻塞住,就无暇顾及其他的命令,这样的影响很大. 2.那么就出现了渐进式遍历,可以做到既能获取所有的key,又不会阻塞服务器.渐进式不是一个命令把所有的key获取到,而是没执行一次命令只…