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

news2024/11/24 22:39:29

半同步半异步线程池

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

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

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

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

在这里插入图片描述

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

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

#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.coloradmin.cn/o/1167683.html

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

相关文章

Windows环境下使用VLC获取到大疆无人机的RTMP直播推流

1.环境准备 1.安装nginx 1.7.11.3 Gryphon 下载地址&#xff1a;http://nginx-win.ecsds.eu/download/ 下载nginx 1.7.11.3 Gryphon.zip&#xff0c;解压后修改文件夹名称为nginx-1.7.11.3-Gryphon&#xff1b; 2.安装nginx-rtmp-module 下载地址&#xff1a;GitHub - arut…

实用-----七牛云绑定自定义域名

实用-----七牛云绑定自定义域名&#xff08;无废话 无尿点&#xff09; 1.打开七牛云 点击自己需要绑定的实例 https://portal.qiniu.com/kodo/bucket 2. 点击域名管理 3.点击添加域名 输入你要绑定的域名 4. 配置 CACHE 复制 CACHE 码 访问腾讯云 CDN 官网 https://console.…

Minium:专业的小程序自动化工具

小程序架构上分为渲染层和逻辑层&#xff0c;尽管各平台的运行环境十分相似&#xff0c;但是还是有些许的区别&#xff08;如下图&#xff09;&#xff0c;比如说JavaScript 语法和 API 支持不一致&#xff0c;WXSS 渲染表现也有不同&#xff0c;所以不论是手工测试&#xff0c…

3D模型格式转换工具HOOPS Exchange:模型的几何数据获取!

3D CAD数据在制造、工程和设计等各个领域都扮演着重要的角色。为了促进不同软件应用程序之间的协作和互操作性&#xff0c;它通常以不同的格式进行交换。HOOPS Exchange是一个强大的软件开发工具包&#xff0c;提供了处理和将3D CAD数据从一种格式转换为另一种格式的解决方案。…

京东商品评论API接口(评论内容|日期|买家昵称|追评内容|评论图片|评论视频..)

京东商品评论API接口是京东开放平台提供的一套API接口&#xff0c;用于获取京东商城商品评论数据。通过该接口&#xff0c;您可以获取到商品评论的详细信息&#xff0c;包括评论内容、评论时间、评论者信息等。 要使用京东商品评论API接口&#xff0c;您需要完成以下步骤&…

C# RFB 人脸识别

C# RFB 人脸识别-CSDN博客 效果 项目 下载 源码下载

uniapp新建的vuecli项目启动报错并且打包失败的问题(已解决)

我的项目新建流程如下 运行之后就是如下报错 解决办法&#xff1a; 安装如下依赖&#xff1a; npm i postcss-loader autoprefixer8.0.0 npm run build 编译失败 安装如下依赖&#xff1a; npm install postcss8.2.2 最终package.json文件如下 {"name": "ls…

Antv/G2 图表坐标轴文字过长时添加省略号

// 格式化文字&#xff0c;超过长度添加省略号chart.axis(city, {label: {formatter: (text) > {// 字符太长添加省略号return text.length > 5 ? ${text.slice(0, 5)}... : text;}}})完整 demo&#xff1a; <!DOCTYPE html> <html lang"en"> &l…

Jetpack Compose 中下拉框实现

下拉菜单主要 以下三种实现&#xff1a; ExperimentalMaterialApi Composable fun ExposedDropdownMenuBox(expanded: Boolean,onExpandedChange: (Boolean) -> Unit,modifier: Modifier Modifier,content: Composable ExposedDropdownMenuBoxScope.() -> Unit )实现代…

云智慧联合北航提出智能运维(AIOps)大语言模型及评测基准

随着各行业数字化转型需求的不断提高&#xff0c;人工智能、云计算、大数据等新技术的应用已不仅仅是一个趋势。各行业企业和组织纷纷投入大量资源&#xff0c;以满足日益挑剔的市场需求&#xff0c;追求可持续性和竞争力&#xff0c;这也让运维行业迎来了前所未有的挑战和机遇…

Apollo云实验:使用Sim control仿真自动驾驶

使用Sim control仿真自动驾驶 概述Sim control仿真自动驾驶启动DreamView仿真系统 实验目的福利活动 主页传送门&#xff1a;&#x1f4c0; 传送 概述 自动驾驶汽车在实现落地应用前&#xff0c;需要经历大量的道路测试来验证算法的可行性和系统的稳定性&#xff0c;但道路测试…

内网穿透配置-Cpolar-Ngrok

文章目录 一、Cpolar1、cpolar软件的使用&#xff1a;&#xff08;1&#xff09;下载与安装&#xff08;2&#xff09;cpolar指定authtoken&#xff08;3&#xff09;获取临时域名&#xff08;4&#xff09;验证临时域名有效性 二、Ngrok1、配置内网穿透&#xff08;1&#xff…

数据挖掘题目:设ε= 2倍的格网间距,MinPts = 6, 采用基于1-范数距离的DBSCAN算法对下图中的实心格网点进行聚类,并给出聚类结果(代码解答)

问题 代码 import matplotlib.pyplot as plt import numpy as np from sklearn.cluster import DBSCAN #pip install matplotlib #pip install numpy #pip install scikit-learn # 实心格网点的坐标 solid_points np.array([[1, 1], [2, 1],[3, 1], [1, 2], [2, 2], [3, 2],[…

【蓝桥杯省赛真题41】Scratch电脑开关机 蓝桥杯少儿编程scratch图形化编程 蓝桥杯省赛真题讲解

目录 scratch电脑开关机 一、题目要求 编程实现 二、案例分析 1、角色分析

ucos_conf、ucos_src和ucos_port

目录 ucos_conf 文件夹ucos_src 文件夹ucos_port 文件夹 在 uC/OS-II 中&#xff0c;ucos_conf、ucos_src 和 ucos_port 是三个不同的文件夹&#xff0c;它们的作用和功能有所不同&#xff1a; ucos_conf 文件夹 ucos_conf 文件夹&#xff1a;ucos_conf 文件夹包含了 uC/OS-II…

高阶数据结构学习 —— 图(4)

文章目录 1、最短路径2、单源最短路径——Dijkstra算法&#xff08;正权值&#xff09;3、单源最短路径——BellmanFord算法1、BF优化&#xff1a;SPFA2、BF算法解决不了带负权回路的问题&#xff0c;实际上哪一个算法都无法求出来 4、多源最短路径——Floyd-Warshall算法 1、最…

汇编-注释

注释有两种说明方法&#xff1a; ●单行注释&#xff0c;用分号(&#xff1b;)开始。汇编器将忽略在同一行上分号之后的所有字符。 ●块注释&#xff0c; 用COMMENT伪指令和一个用户指定的符号开始。汇编器将忽略其后所有 的文本行&#xff0c;直到该用户指定的符号出现为止。…

让学生自助查询成绩的几种方法

在这个信息爆炸的时代&#xff0c;让学生能方便快捷的获取自己的成绩&#xff0c;无疑成为了老师们的一大挑战。我给各位老师介绍几种实用的方法&#xff0c;帮你实现在线发布成绩。 1. 使用在线表格或数据库 使用在线表格或数据库可以让你轻松地存储、管理和发布学生的成绩。…

Python接口自动化测试(接口状态)

本节开始&#xff0c;开始介绍python的接口自动化测试&#xff0c;首先需要搭建python开发环境&#xff0c;到https://www.python.org/下载python 版本直接安装就以了&#xff0c;建议 下载python2.7.11版本&#xff0c;当然&#xff0c;也是可以下载python最新版本的。 接口测…

课题学习(十)----阅读《基于数据融合的近钻头井眼轨迹参数动态测量方法》论文笔记

一、 引言 该论文针对三轴加速度计、磁通门和速率陀螺随钻测量系统&#xff0c;建立了基于四元数井眼轨迹参数测量模型&#xff0c;并依据状态方程和量测方程&#xff0c;应用2个扩卡尔曼滤波器、1个无迹卡尔曼滤波器和磁干扰校正系统对加速度计、磁通门信号进行滤波、校正&…