前言
Golang和传统基于 OS 线程和进程实现不同,Go语言的并发是基于用户态的并发,这种并发方式就变得非常轻量,能够轻松运行几万并发逻辑。
Go 的并发属于 CSP 并发模型的一种实现,CSP 并发模型的核心概念是:“不要通过共享内存来通信,而应该通
过通信来共享内存”。这在 Go 语言中的实现就是 Goroutine 和 Channel。
线程池
- job是任务请求
- worker是线程
- worker pool是线程池
代码实现
Job.Go
任务接口
// Job接口 含一个do方法,实现该接口,即为一个请求任务
type Job interface {
Do()
}
worker.go
线程
// 工作线程结构,里面是一个管道,放多个请求任务
type Worker struct {
JobQueue chan Job
}
// 类似构造函数,返回一个工作线程
func NewWorker() Worker {
return Worker{JobQueue: make(chan Job)}
}
// 工作线程的成员函数,处理请求
// 形参wq是一个工作队列
// 调用之后,会起一个线程,无限循环,取出任务,放到线程池中 ???
func (w Worker) Run(wq chan chan Job) {
go func() {
for {
wq <- w.JobQueue
select {
case job := <-w.JobQueue:
job.Do()
}
}
}()
}
workerpool.go
线程池
// 线程池
// 线程数、任务队列、装任务队列的管道
type WorkerPool struct {
workerlen int
JobQueue chan Job
WorkerQueue chan chan Job
}
// 线程池的构造函数
func NewWorkerPool(workerlen int) *WorkerPool {
return &WorkerPool{
workerlen: workerlen,
JobQueue: make(chan Job),
WorkerQueue: make(chan chan Job, workerlen),
}
}
// 线程池的成员函数
// 先打印初始化
// 根据工人池的长度,循环构造n个工作线程,然后调用工作线程的Run函数,即:把任务放到线程池中,然后处理这个线程的任务请求
// 开一个协程,一直循环,任务队列中取出任务,如果有,则看工作队列是否有空余线程,如果有,则工作队列中,取出一个工作协程,处理job请求
func (wp *WorkerPool) Run() {
fmt.Println("初始化worker")
// 初始化工作线程
for i := 0; i < wp.workerlen; i++ {
worker := NewWorker()
worker.Run(wp.WorkerQueue)
}
// 循环获取可⽤用的工作线程
go func() {
for {
select {
case job := <-wp.JobQueue: // 管道取出任务
worker := <-wp.WorkerQueue // 取出空余线程
worker <- job // 处理任务
}
}
}()
}
main.go
主函数
package main
import (
"fmt"
"runtime"
"time"
)
// 分数结构——打印显示用
type Score struct {
Num int
}
// 分数结构实现了Job接口————即打印自己 然后睡眠2s,即有一个score的请求任务,需要打印自己
func (s *Score) Do() {
fmt.Println("num:", s.Num)
time.Sleep(time.Second)
}
func main() {
//num := 100 * 100 * 20
num := 1
// debug.SetMaxThreads(1000) //设置最⼤大线程数
// 注册⼯工作池,传⼊入任务
// 参数1 worker并发个数
// 长度为20w的工作池
p := NewWorkerPool(num)
// 先运行 挂起
p.Run()
// 创建一亿个分数对象,然后丢进工作池的管道
//datanum := 100 * 100 * 100 * 100
datanum := 1
go func() {
for i := 1; i <= datanum; i++ {
sc := &Score{Num: i}
p.JobQueue <- sc
}
}()
// 每隔2秒打印目前的协程数
for {
fmt.Println("runtime.NumGoroutine() :", runtime.NumGoroutine())
time.Sleep(2 * time.Second)
}
}