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

写在前面

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

  • 核心线程数指的是线程池的基本大小;也就是指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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

KK全域电商,全体系打造实操课,多平台打造电商逻辑体系

实操课详细指导分析流程 资深运营老师陪伴式答疑 课程下载&#xff1a;https://download.csdn.net/download/m0_66047725/89013409 更多资源下载&#xff1a;关注我。 课程内容&#xff1a; 1先导课(拍下请看).mp4 2为什么要做小..红营 .mp4 3小红营适合什么类目的产品,…

linux之文件系统、inode和动静态库制作和发布

一、背景 1.没有被打开的文件都在磁盘上 --- 磁盘级文件 2.对磁盘级别的文件&#xff0c;我们的侧重点 单个文件角度 -- 这个文件在哪里&#xff0c;有多大&#xff0c;其他属性是什么&#xff1f; 站在系统角度 -- 一共有多少文件&#xff1f;各自属性在哪里&#xff1f…

UI自动化框架搭建以及面试题详解(上)

UI自动化框架搭建以及面试题 UI自动化面试题框架面试题那你讲下如何搭建现成的框架公司里面的框架是你搭建的么请结合你的项目讲解一下你的框架是如何搭建的 PO模式什么是 PO 模式PO 模式的封装原则有哪些 DDT驱动模式什么的项目适合ddt ddt四种模式ddt处理各种类型数据 自动化…

软考--软件设计师(软件工程总结3)

目录 1.面向对象技术 2。面向对象分析 3.面向对象程序设计&#xff08;选用一种面向对象的程序语言&#xff09; 4.面向对象测试 5.UML ​编辑6.UML的图 ​编辑7.设计模式 1.面向对象技术 面向对象对象继承类消息通信 对象&#xff1a;是基本运行时的实体&#xff0c;包…

使用Android完成案例教学

目录 题目&#xff1a;完成在Android平台下2个玩家分别利用2个手机连接在同一局域网下通过滑动摇杆分别使红飞机和黄飞机移动的开发。&#xff08;全代码解析&#xff09; 题目&#xff1a;完成在Android平台下2个玩家分别利用2个手机连接在同一局域网下通过滑动摇杆分别使红飞…

Open CASCADE学习|旋转变换

物体在三维空间中的旋转变换操作通常可以通过三种不同的方式来表示&#xff1a;矩阵&#xff08;Matrix&#xff09;、欧拉角&#xff08;Euler Angles&#xff09;和四元数&#xff08;Quaternion&#xff09;。下面详细解释这三种表示方法。 矩阵&#xff08;Matrix&#xf…

【51单片机入门记录】RTC(实时时钟)-DS1302应用

目录 一、DS1302相关写函数 &#xff08;1&#xff09;Write&#xff3f;Ds1302 &#xff08;2&#xff09;Write&#xff3f;Ds1302&#xff3f;Byte 二、DS130相关数据操作流程及相关代码 &#xff08;1&#xff09;DS1302初始化数据操作流程及相关代码 (shijian[i]/10&…

【学习分享】小白写算法之插入排序篇

【学习分享】小白写算法之插入排序篇 前言一、什么是插入排序算法二、插入排序算法如何实现三、C语言实现算法四、复杂度计算五、算法稳定性六、小结 前言 要学好每个算法&#xff0c;我觉得需要先总结出规律&#xff0c;然后自己去推演一遍&#xff0c;加深记忆&#xff0c;否…

阿里云8核32G云服务器租用优惠价格表,包括腾讯云和京东云

8核32G云服务器租用优惠价格表&#xff0c;云服务器吧yunfuwuqiba.com整理阿里云8核32G服务器、腾讯云8核32G和京东云8C32G云主机配置报价&#xff0c;腾讯云和京东云是轻量应用服务器&#xff0c;阿里云是云服务器ECS&#xff1a; 阿里云8核32G服务器 阿里云8核32G服务器价格…

【记录】LangChain|llama 2速通版

官方教程非常长&#xff0c;我看了很认可&#xff0c;但是看完了之后呢就需要一些整理得当的笔记让我自己能更快地找到需求。所以有了这篇文章。【写给自己看的&#xff0c;里面半句废话的解释都没有&#xff0c;如果看不懂的话直接看官方教程再看我的】 我是不打算一开始就用…

Jenkins (四) - 搭建 Docker SonarQube

Jenkins (四) - 搭建 Docker SonarQube 拉取 SonarQube $ docker pull sonarqube拉取 postgres $ $ docker pull postgres运行 postgres $ docker run -itd \ -e TZAsia/Shanghai -e POSTGRES_USERtester \ -e POSTGRES_PASSWORD123456 \ -p 5432:5432 \ -v /home/tester/d…

京东云服务器4核8G主机租用价格418元一年,1899元3年

京东云轻量云主机4核8G服务器租用价格418元一年&#xff0c;1899元3年&#xff0c;配置为&#xff1a;轻量云主机4C8G-180G SSD系统盘-5M带宽-500G月流量&#xff0c;京东云主机优惠活动 yunfuwuqiba.com/go/jd 可以查看京东云服务器详细配置和精准报价单&#xff0c;活动打开如…

【mac操作】brew指令集

brew指令集记录 1. brew search 【软件名称】2. rm -rf $(brew --cache)3. brew install 【软件名】4. brew uninstall 【软件名】5. 未完待续&#xff0c;&#xff0c;&#xff0c;&#xff0c; 官网路径&#xff1a; Homebrew官网 最上面就来一个homebrew安装指令吧&#xf…

win10上一个详细的Django开发入门例子

1.Django概述 Django是一个开放源代码的Web应用框架&#xff0c;由Python写成。采用了MTV的框架模式&#xff0c;即模型M&#xff0c;视图V和模版T。 Django 框架的核心组件有&#xff1a; 用于创建模型的对象关系映射&#xff1b; 为最终用户设计较好的管理界面&#xff1b…

阿里云倚天云服务器详解_CPU采用倚天710处理器

阿里云倚天云服务器CPU采用倚天710处理器&#xff0c;租用倚天服务器c8y、g8y和r8y可以享受优惠价格&#xff0c;阿里云服务器网aliyunfuwuqi.com整理倚天云服务器详细介绍、倚天710处理器性能测评、CIPU架构优势、倚天服务器使用场景及生态支持&#xff1a; 阿里云倚天云服务…

c# wpf LiveCharts MVVM绑定 简单试验

1.概要 c# wpf LiveCharts MVVM绑定 简单试验 2.代码 <Window x:Class"WpfApp3.Window3"xmlns"http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x"http://schemas.microsoft.com/winfx/2006/xaml"xmlns:d"http://s…

gitea简单介绍

Gitea是一个轻量级的开源自托管Git服务&#xff0c;提供了类似GitHub的功能和界面。它是一个简单、易于安装和使用的Git代码托管解决方案&#xff0c;适用于个人、小型团队和企业。 Gitea的主要特点如下&#xff1a; 自托管&#xff1a;Gitea允许在自己的服务器上搭建和管理…

web安全学习笔记(6)

记一下第十节课的内容。 一.PHP语言中的if else判断 语法和c语言中非常类似&#xff0c;不再赘述&#xff0c;也可以使用if...elseif...elseif...else 1.True和False 2.&#xff0c;和 一个等号是赋值 两个等号是比较 三个等号是全等&#xff08;内容相等&#xff0c;数…

调用阿里云API接口实现电商领域命名实体识别NER

文章目录 阿里云简介命名实体识别NER阿里云API注册调用代码阿里云简介 阿里云是全球领先的云计算及人工智能科技公司,成立于2009年,为200多个国家和地区的企业、开发者和政府机构提供服务。阿里云提供了一系列的云计算服务,包括服务器租赁、云数据库、云存储、人工智能等,…

pytest的时候输出一个F后面跟很多绿色的点解读

使用pytest来测试pyramid和kotti项目&#xff0c;在kotti项目测试的时候&#xff0c;输出一个F后面跟很多绿色的点&#xff0c;是什么意思呢&#xff1f; 原来在使用pytest进行测试时&#xff0c;输出中的“F”代表一个失败的测试&#xff08;Failed&#xff09;&#xff0c;而…