😥 整理不易,此资源只针对正式星主开放,
还请入驻星球后再来观看。

GOLANG ROADMAP

阅读模式

  • 沉浸
  • 自动
  • 日常
首页
Go学习
  • Go学院

    • Go小课
    • Go视界
    • Go小考
    • Go实战
  • Go资源

    • 优质课程
    • 在线宝典
    • 资源下载
    • 帮找资源
训练营 🔥
  • Go体系课&实战训练营
  • 升值加薪陪跑训练营
Go求职
  • 求职刷题

    • 企业题库
    • 面试宝典
    • 求职面经
  • 求职服务

    • 内推互助
    • 求职助力
    • 内推公司
Go友会
  • 城市
  • 校园
推广返佣
  • 返佣排行
  • 返佣规则
  • 推广学院
实验区
  • Go周边
  • Go宝典

    • 推荐图书
    • 精品博文
  • Go开源

    • Go仓库
    • Go月刊
更多
  • 用户中心

    • 我的信息
    • 我的返佣
    • 我的消息
  • 玩转星球

    • 星球介绍
    • 星主权益
    • 吐槽专区
    • 成长记录
  • 合作交流

    • 商务合作
    • 讲师招募
    • 生态伙伴
author-avatar

GOLANG ROADMAP


首页
Go学习
  • Go学院

    • Go小课
    • Go视界
    • Go小考
    • Go实战
  • Go资源

    • 优质课程
    • 在线宝典
    • 资源下载
    • 帮找资源
训练营 🔥
  • Go体系课&实战训练营
  • 升值加薪陪跑训练营
Go求职
  • 求职刷题

    • 企业题库
    • 面试宝典
    • 求职面经
  • 求职服务

    • 内推互助
    • 求职助力
    • 内推公司
Go友会
  • 城市
  • 校园
推广返佣
  • 返佣排行
  • 返佣规则
  • 推广学院
实验区
  • Go周边
  • Go宝典

    • 推荐图书
    • 精品博文
  • Go开源

    • Go仓库
    • Go月刊
更多
  • 用户中心

    • 我的信息
    • 我的返佣
    • 我的消息
  • 玩转星球

    • 星球介绍
    • 星主权益
    • 吐槽专区
    • 成长记录
  • 合作交流

    • 商务合作
    • 讲师招募
    • 生态伙伴
  • Go真实面试题汇总系列

    • Go基础篇
  • 宝典内容

    • 20. 2个协程交替打印字母和数字
    • 21. goroutine与线程的区别?
    • 27. 为什么不要大量使用goroutine
    • 38. 协程goroutine
    • 41. go 中用 for 遍历多次执行 goroutine会存在什么问题
    • 49. 问等待所有goroutine结束,怎么做?
    • 56. goroutine 为什么轻量
    • 86. 用Channel和两个协程实现数组相加
    • 98. go协程的实现方式
    • 106. 并行goroutine如何实现
    • 109. goroutine和线程的区别,为什么说goroutine轻量
    • 117. 父 goroutine 退出,如何使得子 goroutine 也退出?
    • 125. 主协程如何等待其余协程完再操作
    • 141. 为什么不要频繁创建和停止goroutine
    • 146. 如何拿到多个goroutine的返回值,如何区别他们
    • 151.golang goroutine的工作原理以及他们怎么进行数据交互的
    • 157. 一个线程打印奇数一个线程打印偶数 交替打印
    • 160. channel主要做什么事情
    • 167. golang 协程机制
    • 168. 协程的栈空间大小有限制吗?会主动扩展吗?
    • 169. 用go实现一个协程池,大概用什么实现
    • 170. go里面为什么需要多协程?
    • 171. goroutine为什么会存在,为什么不使用线程?
    • 173. go协程线程进程区别
    • 176. go协程相比其它协程库区别在哪?
    • 184. 编程go协程交叉顺序打印数组
    • 185. go协程通信
    • 203. 一个goroutine sleep了,操作系统是怎么唤醒的
    • 222. Go的协程可以不可以自己让出cpu
    • 223. Go的协程可以只挂在一个线程上面吗
    • 239. 用go撸一个生产者消费型,用channel通信,怎么友好的关闭chan?
    • 240. goroutine调度源码
    • 248. go实现协程池
    • 249. 两个协程交替打印1到20
    • 251. goroutine 和 kernel thread 之间是什么关系?
    • 254. 用过go,那么进程,协程,线程各自的优缺点
    • 264. Go的多线程
    • 268. 进程和协程
    • 269. 如何解决孤儿进程的出现
    • 274. goroutine为什么比线程开销小,实现原理
    • 277. 协程实现顺序打印123
    • 286. goroutine的调度是出现在什么情况下,调度时做了什么
    • 297. golang有什么提高性能的设计, 重点说说goroutine
    • 298. 进程、线程和协程和通信方式
    • 300. Goroutine 数量是越多越好吗?
    • 308. go协程的简单用法
    • 314. 为什么用户级别的线程 goroutine 比操作系统线程更轻量级?
    • 341. 协程怎么停顿?
    • 367. go 如何关闭goroutine
    • 373. Go 语言协程怎么跑的
    • 375. Go创建协程的过程
    • 376. 协程共享哪些资源?
    • 385. go中协程是如何实现的
    • 386. 协程中参数直接使用,和传参的区别是什么,为什么会造成这种结果
    • 389. 是否写过go语言多协程内容
    • 394. 开俩个协程,一个协程生产数据,另一个协程对数据进行处理,处理完后再把数据发回去,使用管道如何实现?
    • 406. goroutine泄露
    • 407. 如何停止一个goroutine
    • 410. 查看goroutine (579)
    • 420. 用go协程的时候也是要走IO的,go是如何处理的?
    • 444. 有没有了解过goroutine的底层数据结构, 为什么协程比线程轻量且快
    • 461. 如何限制 goroutine 并发数量 (channel 或 WaitGroup)
    • 465. Go里面一个协程能保证绑定在一个内核线程上面的。

😥 整理不易,此资源只针对正式星主开放,
还请入驻星球后再来观看。

169. 用go实现一个协程池,大概用什么实现


企业题库解析小组

题目序号:773

题目来源:网易

频次:1

# 答案:Carpe-Wang

定义一个task 的结构体 标示具体要执行的任务格式

type Job func([]interface{})type taskWork struct {
	    Run       Job	
        startBool bool	
        params    []interface{}
}
1
2
3
4
5

定义一个worker 池,控制协程相关信息

type WorkPool struct {
	taskPool  chan taskWork
	workNum   int
	maxNum    int
	stopTopic bool
	//考虑后期 作为冗余队列使用
	taskQue chan taskWork
}
1
2
3
4
5
6
7
8

实现协程池相关启动,停止,扩容策略,缩减策略,备用队列启用等 逻辑

//得到一个线程池并返回 句柄
func (p *WorkPool) InitPool() {
	*p = WorkPool{workNum: workerNumDefault,
		maxNum: workerNumMax, stopTopic: false,
		taskPool: make(chan taskWork, workerNumDefault*2), taskQue: nil}
 
	(p).start()
	go (p).workerRemoveConf()
}
 
//开始work
func (p *WorkPool) start() {
	for i := 0; i < workerNumDefault; i++ {
		p.workInit(i)
		fmt.Println("start pool task:", i)
	}
}
 
//初始化 work池 后期应该考虑如何 自动 增减协程数,以达到最优
func (p *WorkPool) workInit(id int) {
	go func(idNum int) {
		//var i int = 0
		for {
			select {
			case task := <-p.taskPool:
				if task.startBool == true && task.Run != nil {
					//fmt.Print("this is pool ", idNum, "---")
					task.Run(task.params)
				}
				//单个结束任务
				if task.startBool == false {
					//fmt.Print("this is pool -- ", idNum, "---")
					return
				}
				//防止从channal 中读取数据超时
			case <-time.After(time.Millisecond * 1000):
				//fmt.Println("time out init")
				if p.stopTopic == true && len(p.taskPool) == 0 {
					fmt.Println("topic=", p.stopTopic)
					//work数递减
					p.workNum--
					return
				}
				//从备用队列读取数据
			case queTask := <-p.taskQue:
				if queTask.startBool == true && queTask.Run != nil {
					//fmt.Print("this is que ", idNum, "---")
					queTask.Run(queTask.params)
				}
			}
 
		}
	}(id)
 
}
 
//停止一个workPool
func (p *WorkPool) Stop() {
	p.stopTopic = true
}
 
//普通运行实例,非自动扩充
func (p *WorkPool) Run(funcJob Job, params ...interface{}) {
	p.taskPool <- taskWork{funcJob, true, params}
}
 
//用select 去做 实现 自动扩充 协程个数 启用备用队列等特性
func (p *WorkPool) RunAuto(funcJob Job, params ...interface{}) {
	task := taskWork{funcJob, true, params}
	select {
	//正常写入
	case p.taskPool <- task:
		//写入超时 说明队列满了 写入备用队列
	case <-time.After(time.Millisecond * 1000):
		p.taskQueInit()
		p.workerAddConf()
		//task 入备用队列
		p.taskQue <- task
	}
}
 
//自动初始化备用队列
func (p *WorkPool) taskQueInit() {
	//扩充队列
	if p.taskQue == nil {
		p.taskQue = make(chan taskWork, p.maxNum*2)
	}
}
 
//自动扩充协程 简单的自动扩充策略
func (p *WorkPool) workerAddConf() {
	//说明需要扩充进程  协程数量小于 1000 协程数量成倍增长
	if p.workNum < 1000 {
		p.workerAdd(p.workNum)
	} else if p.workNum < p.maxNum {
		tmpNum := p.maxNum - p.workNum
		tmpNum = tmpNum / 10
		if tmpNum == 0 {
			tmpNum = 1
		}
		p.workerAdd(1)
	}
}
 
//自动缩减协程 实现比较粗糙,可以考虑后续精细实现一些策略
func (p *WorkPool) workerRemoveConf() {
	for {
		select {
		case <-time.After(time.Millisecond * 1000 * 600):
			if p.workNum > workerNumDefault && len(p.taskPool) == 0 && len(p.taskQue) == 0 {
				rmNum := (p.workNum - workerNumDefault) / 5
				if rmNum == 0 {
					rmNum = 1
				}
				p.workerRemove(rmNum)
			}
		}
	}
 
}
func (p *WorkPool) workerAdd(num int) {
	for i := 0; i < num; i++ {
		p.workNum++
		p.workInit(p.workNum)
	}
}
func (p *WorkPool) workerRemove(num int) {
	for i := 0; i < num; i++ {
		task := taskWork{startBool: false}
		p.taskPool <- task
		p.workNum--
	}
}
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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
  • 答案:Carpe-Wang