Golang 线程 和 协程 的区别
对于进程、线程,都是有内核进行调度,有 CPU 时间片的概念,进行抢占式调度(有多种调度算法)
对于协程(用户级线程),这是对内核透明的,也就是系统并不知道有协程的存在,是完全由用户自己的程序进行调度的,因为是由用户程序自己控制,那么就很难像抢占式调度那样做到强制的 CPU 控制权切换到其他进程/线程,通常只能进行协作式调度,需要协程自己主动把控制权转让出去之后,其他协程才能被执行到。
goroutine 和 协程 区别
本质上,goroutine 就是协程。不同的是,Golang 在 runtime、系统调用等多方面对 goroutine 调度进行了封装和处理,当遇到长时间执行或者进行系统调用时,会主动把当前 goroutine 的CPU § 转让出去,让其他 goroutine 能被调度并执行,也就是 Golang 从语言层面支持了协程。Golang 的一大特色就是从语言层面原生支持协程,在函数或者方法前面加 go关键字就可创建一个协程。
- 内存消耗方面
每个 goroutine (协程) 默认占用内存远比 Java 、C 的线程少。
goroutine:2KB
线程:8MB - 线程和 goroutine 切换调度开销方面
线程/goroutine 切换开销方面,goroutine 远比线程小
线程:涉及模式切换(从用户态切换到内核态)、16个寄存器、PC、SP…等寄存器的刷新等
goroutine:只有三个寄存器的值修改 - PC / SP / DX
由于协程是非抢占式的调度,无法实现公平的任务调用,也无法直接利用多核优势。因此,我们不能武断地说协程是比线程更高级的技术。
协程池实现golang中启动一个协程不会消耗太多资源,有人认为可以不用协程池。但是当访问量增大时,可能造成内存消耗完,程序崩溃。写了一个协程池的Demo。
定义接口体:
Pool : 定义goroutine相关控制参数
Job:根据应用场景传入需要处理的对象
Work:加工处理Job对象
package main
import (
"fmt"
"time"
)
type Pool struct {
JobQueue chan Job //待处理的任务队列
WorkerCurrentNum int //当前正在工作的协程数
MaxWorker int //允许最大工作协程数
Result chan int //处理完成的任务结果队列
resultNum int //已处理任务数
}
type Job struct {
ID int
}
type Worker struct {
Result chan int
}
func (w *Worker) DoJob(job Job) {
//fmt.Println(job.ID)
fmt.Println("worker started job", job.ID)
w.Result <- job.ID
fmt.Println("worker finished job", job.ID)
}
// 往Job任务队列里面放入待处理的job
func (g *Pool) AddJob(job Job) {
g.JobQueue <- job
}
func (g *Pool) stop() {
for {
c1 := <- g.Result
fmt.Println("接收job", c1, "已处理结果")
g.WorkerCurrentNum --
g.resultNum ++
}
}
// 开启协程池
func (g *Pool) Run() {
go g.stop()
outLoop:
for {
if g.WorkerCurrentNum < g.MaxWorker {
select {
// data, ok := <-ch 非阻塞接收数据
// data:表示接收到的数据。未接收到数据时,data 为通道类型的零值。
// ok:表示是否接收到数据。
case job, ok := <- g.JobQueue:
if ok {
fmt.Println("Waiting for job...")
worker := &Worker{g.Result}
go worker.DoJob(job)
g.WorkerCurrentNum++
}else {
break outLoop //JobQueue已经关闭,不需要再创建Worker协程
}
}
}
}
//保证JobQueue中18个任务全部被处理时退出run
for g.resultNum != 18 {
time.Sleep(time.Second)
}
}
func main() {
jobQueue := make(chan Job)
resultQueue := make(chan int)
p := &Pool{
MaxWorker: 5,
JobQueue: jobQueue,
Result: resultQueue,
resultNum: 0,
}
go func() {
for i:= 0; i < 18; i++{
job := Job{i}
p.AddJob(job)
}
close(p.JobQueue)
}()
p.Run()
fmt.Println("Complete main")
}
我是pavel,一位憨憨傻傻的程序员,平时幽默又有才,专注于Java,go,微服务,云开发。不定时发送些腾讯程序员的工作/生活日常,请大家多多关注我的公众号!