线程池详解并使用Go语言实现 Pool

news2024/11/19 8:48:01

写在前面

在线程池中存在几个概念:核心线程数最大线程数任务队列

  • 核心线程数指的是线程池的基本大小;也就是指worker的数量
  • 最大线程数指的是,同一时刻线程池中线程的数量最大不能超过该值;实际上就是指task任务的数量。
  • 任务队列是当任务较多时,线程池中线程的数量已经达到了核心线程数,这时候就是用任务队列来存储我们提交的任务。相当于缓冲作用。

与其他池化技术不同的是,线程池是基于生产者-消费者模式来实现的,任务的提交方是生产者,线程池是消费者 。当我们需要执行某个任务时,只需要把任务扔到线程池中即可。

池化技术:这里的池化和卷积的池化不一样,这里的池化技术简单点来说,就是提前保存大量的资源,以备不时之需

线程池中执行任务的流程如下图如下。
在这里插入图片描述

那么使用线程池可以带来一系列好处:

  1. 降低资源消耗:通过池化技术重复利用已创建的线程,降低线程创建和销毁造成的损耗。
  2. 提高响应速度:任务到达时,无需等待线程创建即可立即执行
  3. 提高线程的可管理性:线程是稀缺资源,如果无限制创建,不仅会消耗系统资源,还会因为线程的不合理分布导致资源调度失衡,降低系统的稳定性。使用线程池可以进行统一的分配、调优和监控。
  4. 提供更多更强大的功能:线程池具备可拓展性,允许开发人员向其中增加更多的功能。

任务调度

首先检测线程池运行状态,如果不是RUNNING,则直接拒绝,线程池要保证在RUNNING的状态下执行任务。

  1. 如果 taskCount < corePoolSize,则创建并启动一个线程来执行新提交的任务。

在这里插入图片描述

  1. 如果 taskCount >= corePoolSize,且线程池内的阻塞队列未满,则将任务添加到该阻塞队列中。

在这里插入图片描述

  1. 如果workerCount >= maximumPoolSize,并且线程池内的阻塞队列已满, 则根据 拒绝策略 来处理该任务, 默认的处理方式是直接抛异常。

在这里插入图片描述

常见的拒绝策略有以下几种

  • AbortPolicy 中止策略:丢弃任务并抛出异常
  • DiscardPolicy 丢弃策略:丢弃任务,但是不抛出异常。如果线程队列已满,则后续提交的任务都会被丢弃,且是静默丢弃。
  • DiscardOldestPolicy 弃老策略:丢弃队列最前面的任务,然后重新提交被拒绝的任务。

简单实现

定义任务Task 并 定义NewTask来新建Task对象

type Task struct {
	f func() error
}

func NewTask(f func() error) *Task {
	return &Task{f: f}
}

定义 WorkPool 线程池

type WorkPool struct {
	TaskQueue chan *Task // Task队列
	workNum   int        // 协程池中最大的worker数量
	shop      chan struct{} // 停止工作标识
}

创建 WorkPool 的函数

func NewWorkPool(cap int) *WorkPool {
	if cap <= 0 {
		cap = 10
	}
	return &WorkPool{
		TaskQueue: make(chan *Task),
		workNum:   cap,
		shop:      make(chan struct{}),
	}
}

具体的协程池中的工作节点

func (p *WorkPool) worker(workId int) {
	for task := range p.TaskQueue {
		err := task.Execute()
		if err != nil {
			fmt.Println(err)
			continue
		}
		fmt.Printf(" work id %d finished \n", workId) // 打印出具体是哪个节点进行工作
	}
}

协程池启动函数

func (p *WorkPool) run() {
	// 根据work num 去创建worker工作
	for i := 0; i < p.workNum; i++ {
		go p.worker(i)
	}
	<-p.shop
}

协程池关闭函数

func (p *WorkPool) close() {
	p.shop <- struct{}{}
}

测试一下,使用定时器,每2秒进行一次投放,并且投放超过5个之后开始停止。

func TestWorkPool(t *testing.T) {
	task := NewTask(func() error {
		fmt.Print(time.Now())
		return nil
	})
	taskCount := 0
	ticker := time.NewTicker(2 * time.Second)
	p := NewWorkPool(3)
	go func(c *time.Ticker) {
		for {
			p.TaskQueue <- task
			<-c.C
			taskCount++
			if taskCount == 5 {
				p.close()
				break
			}
		}
		return
	}(ticker)
	p.run()
}

结果:

可以看到结果是每两秒进行一次打印,并且worker对象都不一样。

完整代码

package gorountine_pool

import (
	"fmt"
	"testing"
	"time"
)

func TestWorkPool(t *testing.T) {
	task := NewTask(func() error {
		fmt.Print(time.Now())
		return nil
	})
	taskCount := 0
	ticker := time.NewTicker(2 * time.Second)
	p := NewWorkPool(3)
	go func(c *time.Ticker) {
		for {
			p.TaskQueue <- task
			<-c.C
			taskCount++
			if taskCount == 5 {
				p.close()
				break
			}
		}

		return
	}(ticker)
	p.run()
}

type Task struct {
	f func() error
}

func NewTask(f func() error) *Task {
	return &Task{f: f}
}

// Execute 执行业务方法
func (t *Task) Execute() error {
	return t.f()
}

type WorkPool struct {
	TaskQueue chan *Task // task队列
	workNum   int        // 携程池中最大的worker数量
	shop      chan struct{} // 停止标识
}

// 创建Pool的函数
func NewWorkPool(cap int) *WorkPool {
	if cap <= 0 {
		cap = 10
	}
	return &WorkPool{
		TaskQueue: make(chan *Task),
		workNum:   cap,
		shop:      make(chan struct{}),
	}
}

func (p *WorkPool) worker(workId int) {
	// 具体的工作
	for task := range p.TaskQueue {
		err := task.Execute()
		if err != nil {
			fmt.Println(err)
			continue
		}
		fmt.Printf(" work id %d finished \n", workId)
	}
}

// 携程池开始工作
func (p *WorkPool) run() {
	// 根据work num 去创建worker工作
	for i := 0; i < p.workNum; i++ {
		go p.worker(i)
	}
	<-p.shop
}

func (p *WorkPool) close() {
	p.shop <- struct{}{}
}

参考链接

[1] https://tech.meituan.com/2020/04/02/java-pooling-pratice-in-meituan.html
[2] https://blog.csdn.net/weixin_44688301/article/details/123292211
[3] https://www.bilibili.com/video/BV1Nf4y137na

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

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

相关文章

9_springboot_shiro_jwt_多端认证鉴权_整合jwt

1. Shiro框架回顾 到目前为之&#xff0c;Shiro框架本身的知识点已经介绍完了。web环境下&#xff0c;整个框架从使用的角度我们需要关注的几个点&#xff1a; 要使用Shiro框架&#xff0c;就要创建核心部件securityManager 对象。 SpringBoot项目中&#xff0c;引入shiro-spr…

python接入AI 实现微信自动回复

import numpy as np # 引入numpy库&#xff0c;目的是将读取的数据转换为列表 import pandas as pd # 引入pandas库&#xff0c;用来读取csv数据 from uiautomation import WindowControl # 引入uiautomation库中的WindowControl类&#xff0c;用来进行图像识别和模拟操作 i…

go | 上传文件分析 | http协议分析 | 使用openssl 实现 https 协议 server.key、server.pem

是这样的&#xff0c;现在分析抓包数据 test.go package mainimport ("fmt""log""github.com/gin-gonic/gin" )func main() {r : gin.Default()// Upload single filer.MaxMultipartMemory 8 << 20r.POST("/upload", func(c *g…

Apache Log4j2 Jndi RCE CVE-2021-44228漏洞原理讲解

Apache Log4j2 Jndi RCE CVE-2021-44228漏洞原理讲解 一、什么是Log4j2二、环境搭建三、简单使用Log4j2四、JDNI和RMI4.1、启动一个RMI服务端4.2、启动一个RMI客户端4.3、ldap 五、漏洞复现六、Python批量检测 参考视频&#xff1a;https://www.bilibili.com/video/BV1mZ4y1D7K…

基于Socket简单的TCP网络程序

⭐小白苦学IT的博客主页 ⭐初学者必看&#xff1a;Linux操作系统入门 ⭐代码仓库&#xff1a;Linux代码仓库 ❤关注我一起讨论和学习Linux系统 TCP单例模式的多线程版本的英汉互译服务器 我们先来认识一下与udp服务器实现的不同的接口&#xff1a; TCP服务器端 socket()&…

【C++初阶】String在OJ中的使用(一):仅仅反转字母、字符串中的第一个唯一字母、字符串最后一个单词的长度、验证回文串、字符串相加

前言&#xff1a; &#x1f3af;个人博客&#xff1a;Dream_Chaser &#x1f388;博客专栏&#xff1a;C &#x1f4da;本篇内容&#xff1a;仅仅反转字母、字符串中的第一个唯一字母、字符串最后一个单词的长度、验证回文串、字符串相加 目录 917.仅仅反转字母 题目描述&am…

【stm32】软件I2C读写MPU6050

软件I2C读写MPU6050(文章最后附上源码) 编码 概况 首先建立通信层的.c和.h模块 在通信层里写好I2C底层的GPIO初始化 以及6个时序基本单元 起始、终值、发送一个字节、接收一个字节、发送应答、接收应答 写好I2C通信层之后&#xff0c;再建立MPU6050的.c和.h模块 基于I2C通…

软考116-上午题-【计算机网络】-LINUX命令

一、真题 真题1&#xff1a; 真题2&#xff1a; 权限通常分为三类&#xff1a; 读&#xff08;r&#xff09;&#xff1a;允许读取文件内容或列出目录内容。写&#xff08;w&#xff09;&#xff1a;允许修改文件内容或在目录中创建/删除文件。执行&#xff08;x&#xff09;&…

stm32开发之threadx使用记录(主逻辑分析)

前言 threadx的相关参考资料 论坛资料、微软官网本次使用的开发板为普中科技–麒麟&#xff0c;核心芯片为 stm32f497zgt6开发工具选择的是stm32cubemx(代码生成工具)clion(代码编写工具)编译构建环境选择的是arm-none-gcc编译 本次项目结构 CMakeList对应的配置 set(CMAKE_…

SD-WAN国际网络专线:高效、合规且可靠的跨境连接解决方案

在数字化时代&#xff0c;企业对跨境网络连接的需求日益增长。SD-WAN技术作为一种新兴的解决方案&#xff0c;正逐渐成为构建跨境网络连接的首选。本文将探讨SD-WAN国际网络专线的发展现状、合规性要求以及选择时需要考虑的关键因素。 SD-WAN技术&#xff1a;跨境网络连接的新…

如何在没有备份的情况下从 iPad 恢复照片?

有很多操作都可能导致iPad照片丢失&#xff0c;包括误删除、出厂设置、iPad的iOS更新等。如果没有备份&#xff0c;似乎没有办法找回它们。然而&#xff0c;即使您将备份保留在 iCloud 或iTunes上&#xff0c;这些方式也需要您的 iPad 首先重置&#xff0c;从而用备份内容覆盖当…

堆排序解读

在算法世界中&#xff0c;排序算法一直是一个热门话题。推排序&#xff08;Heap Sort&#xff09;作为一种基于堆这种数据结构的有效排序方法&#xff0c;因其时间复杂度稳定且空间复杂度低而备受青睐。本文将深入探讨推排序的原理、实现方式&#xff0c;以及它在实际应用中的价…

lua学习笔记5(分支结构和循环的学习)

print("*****************分支结构和循环的学习******************") print("*****************if else语句******************") --if 条件 then end a660 b670 --单分支 if a<b thenprint(a) end --双分支 if a>b thenprint("满足条件")…

机器学习模型——逻辑回归

https://blog.csdn.net/qq_41682922/article/details/85013008 https://blog.csdn.net/guoziqing506/article/details/81328402 https://www.cnblogs.com/cymx66688/p/11363163.html 参数详解 逻辑回归的引出&#xff1a; 数据线性可分可以使用线性分类器&#xff0c;如果…

c# wpf LiveCharts 简单试验

1.概要 1.1 说明 1.2 环境准备 NuGet 添加插件安装 2.代码 <Window x:Class"WpfApp3.MainWindow"xmlns"http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x"http://schemas.microsoft.com/winfx/2006/xaml"xmlns:d"…

WindowsPowerShell安装配置Vim的折腾记录

说明 vim一直以来都被称为编辑器之神一样的存在。但用不用vim完全取决于你自己&#xff0c;但是作为一个学计算机的同学来说&#xff0c;免不了会和Linux打交道&#xff0c;而大部分的Linux操作系统都预装了vim作为编辑器&#xff0c;如果是简单的任务&#xff0c;其实vim只要会…

电商技术揭秘八:搜索引擎中的SEO内部链接建设与外部推广策略

文章目录 引言一、 内部链接结构优化1.1 清晰的导航链接1. 简洁明了的菜单项2. 逻辑性的布局3. 避免深层次的目录结构4. 使用文本链接5. 突出当前位置6. 移动设备兼容性 1.2 面包屑导航1. 显示当前页面位置2. 可点击的链接3. 简洁性4. 适当的分隔符5. 响应式设计6. 避免重复主页…

图像分割-RSPrompter

文章目录 前言1. 自动化提示器1.1 多尺度特征增强器1.2 RSPrompterAnchor-based PrompterQuery-based Prompter 2. SAM的扩展3. 结果WHU数据集NWPU数据集SSDD数据集 前言 《RSPrompter: Learning to prompt for remote sensing instance segmentation based on visual foundati…

Linux--03---虚拟机网络配置、拍摄快照和克隆

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 1.虚拟机网络配置1.虚拟机的联网模式模式1 仅主机模式特点模式2 桥接模式特点模式3 NAT模式特点关于模式的选择 2. 修改网络配置信息3.修改虚拟机ens33网卡的网络配…

「 典型安全漏洞系列 」12.OAuth 2.0身份验证漏洞

在浏览网页时&#xff0c;你肯定会遇到允许你使用社交媒体帐户登录的网站。此功能一般是使用流行的OAuth 2.0框架构建的。本文主要介绍如何识别和利用OAuth 2.0身份验证机制中发现的一些关键漏洞。 1. OAuth产生背景 为了更好的理解OAuth&#xff0c;我们假设有如下场景&#…