惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

量子位
F
Fortinet All Blogs
小众软件
小众软件
人人都是产品经理
人人都是产品经理
The Cloudflare Blog
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Microsoft Azure Blog
Microsoft Azure Blog
J
Java Code Geeks
有赞技术团队
有赞技术团队
D
DataBreaches.Net
Hugging Face - Blog
Hugging Face - Blog
V
Visual Studio Blog
A
About on SuperTechFans
I
InfoQ
The GitHub Blog
The GitHub Blog
Engineering at Meta
Engineering at Meta
雷峰网
雷峰网
H
Hackread – Cybersecurity News, Data Breaches, AI and More
罗磊的独立博客
C
Check Point Blog
大猫的无限游戏
大猫的无限游戏
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
酷 壳 – CoolShell
酷 壳 – CoolShell
MyScale Blog
MyScale Blog

博客园 - 立志做一个好的程序员

【转】Golang使用时区时候报错 详解 Go 中的 rune 类型 【转】扫盲:Windows桌面应用开发框架:原生、跨平台、云桌面 Go-Zero自定义goctl实战:定制化模板,加速你的微服务开发效率 Go单体服务开发最佳实践 【转】Androidstudio报错Algorithm HmacPBESHA256 not available 借助ai来分析代码,理解代码 【转】linux设置密钥登录(只允许密钥登录) 【转】QPS和并发数的关系 【转】Command Pattern in Go (Golang) [转]Golang Functional Options Pattern 【转】Raft 理论基础 【转】spin lock 和mutex [转]Go sync.Once:简约而不简单的并发利器 [转]Golang atomic.CompareAndSwapInt64()实例讲解 [转]手把手教你如何用golang实现一个timewheel时间轮 [转]Go网络编程 · 一条TCP连接讲透九大知识点 【转】,接上面3篇.Implement Sql Database Driver in 100 Lines of Go 【转】dive into golang database/sql(3)
[转]Golang线程池实现百万级高并发
立志做一个好的程序员 · 2023-11-19 · via 博客园 - 立志做一个好的程序员

本文基于Golang实现线程池,从而可以达到百万级别的高并发请求。本文实现的代码在https://github.com/lk668/threadpool可见。

1. Golang并发简介

Golang原生的goroutine可以很轻松实现并发编程。Go语言的并发是基于用户态的并发,这种并发方式就变得非常轻量,能够轻松运行几万并发逻辑。Go 的并发属于 CSP 并发模型的一种实现,CSP 并发模型的核心概念是:不要通过共享内存来通信,而应该通过通信来共享内存。

2. 并发方案演进

2.1 直接使用goroutine

直接使用goroutine启动一个新的线程来进行任务的执行

2.2 缓冲队列

利用channel实现一个缓冲队列,每次请求先放入缓冲队列,然后从缓冲队列读取数据,一次执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
type Job interface{
Run()
}

该方案在请求量低于缓冲队列长度时,可以应对并发请求。但是当并发请求量大于缓冲队列长度时,channel会出现阻塞情况。

2.3 线程池实现百万级高并发

更好的实现方案是利用job队列+线程池来实现,具体如下所示:

有个全局JobQueue,用来存储需要执行的Job,有个WorkerPool的线程池,用来存储空闲的Worker。当JobQueue中有Job时,从JobQueue获取Job对象,从WorkerPool获取空闲Worker,将Job对象发送给Worker,进行执行。每个Worker都是一个独立的Goroutine。从而真正意义上实现高并发。

代码实现主要分为三部分

2.3.1 Job定义

Job是一个interface,其下有个函数RunTask,用户定制化的任务,需要实现RunTask函数。JobChan是一个Job channel结构。Job是高并发需要执行的任务

1
2
3
4
5
type Job interface {
RunTask(request interface{})
}

type JobChan chan Job

2.3.2 Worker定义

Worker就是高并发里面的一个线程,启动的时候是一个Goroutine。Worker结构一需要一个JobChan,用来接收从全局JobQueue里面获取的Job对象。有个Quit的channel,用来接收退出信号。需要一个Start函数,将自己注册到WorkerPool,然后监听Job,有Job传入时,处理Job的RunTask,处理完成之后,重新将自己添加回WorkerPool。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

2.3.3 WorkerPool定义

WorkerPool是一个线程池,用来存储Worker。所以需要一个Size变量,用来表示这个Pool存储的Worker的个数。需要一个JobQueue,用来充当全局JobQueue的作用,所有的job先存储到该全局JobQueue中。有个WorkerQueue是个channel,用来存储空闲的Worker,有Job来的时候,从WorkerQueue里面取出一个Worker,执行相应的任务,执行完成以后,重新将Worker放回WorkerQueue。

WorkerPool需要一个启动函数,一个是来启动Size数量的Worker线程,一个是需要启动一个新的线程,来接收Job。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38

2.3.4 代码调用举例

接下来,举例分析如何使用该线程池。首先需要定义你要执行的任务,实现RunTask函数。然后初始化一个WorkerPool,将模拟百万请求的数据发送给全局JobQueue。交给线程池进行任务处理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

参考