关键知识点:
基础知识:
线程与进程:进程是资源分配的最小单位,线程是独立运行的基本单位
并行与并发:同一个cpu上执行叫并发、多个cpu上执行叫并行
协程于线程:协程是栈私有,堆共享,调度自己控制。协程是轻量级的线程。(java中调度可以是jvm、抢占式等外界力量对其调度)
核心知识点:
goroutine: 官方定义的“线程池”,运行时候调度管理
内部的调度GMP(goroutine、Processor、Machine):Processor就是个队列,思路就是解耦,有了P的存在,就将g和m分开了,使得两者独立。本质上和线程池思路没啥区别
runtime包:
goSched让出cpu对标java中的sleep,cpu没有,资源应该还在手?
goExit就是协程退出,对标java的stop。一般来说不会粗鲁的停止对应的协程
goMaxProcs 最大的核心os数,对标线程池的最大核心线程数
channel:实现通过通信共享内存不是通过共享内存来实现通信。(简单理解为mq)
channel := make(chan string, 10)
panic出现的情况:读取已经关闭的通道、关闭已经关闭的通道
select:类似switch,哪个通道读取成功,执行哪个
互斥: sync.Mutex/RWMutex, lock and unlock
sync.waitGroup(n), add, done, wait
sync.once only run once
sync.map 线程安全的map
atomic:主要是操作一些基础类型,进行一些原子操作
经典demo:
channel通道:
import (
"fmt"
"math/rand"
)
type Job struct {
// id
Id int
// 需要计算的随机数
RandNum int
}
type Result struct {
// 这里必须传对象实例
job *Job
// 求和
sum int
}
func main() {
// 需要2个管道
// 1.job管道
jobChan := make(chan *Job, 128)
// 2.结果管道
resultChan := make(chan *Result, 128)
// 3.创建工作池
createPool(64, jobChan, resultChan)
// 4.开个打印的协程
go func(resultChan chan *Result) {
// 遍历结果管道打印
for result := range resultChan {
fmt.Printf("job id:%v randnum:%v result:%d\n", result.job.Id,
result.job.RandNum, result.sum)
}
}(resultChan)
var id int
// 循环创建job,输入到管道
for {
id++
// 生成随机数
r_num := rand.Int()
job := &Job{
Id: id,
RandNum: r_num,
}
jobChan <- job
}
}
// 创建工作池
// 参数1:开几个协程
func createPool(num int, jobChan chan *Job, resultChan chan *Result) {
// 根据开协程个数,去跑运行
for i := 0; i < num; i++ {
go func(jobChan chan *Job, resultChan chan *Result) {
// 执行运算
// 遍历job管道所有数据,进行相加
for job := range jobChan {
// 随机数接过来
r_num := job.RandNum
// 随机数每一位相加
// 定义返回值
var sum int
for r_num != 0 {
tmp := r_num % 10
sum += tmp
r_num /= 10
}
// 想要的结果是Result
r := &Result{
job: job,
sum: sum,
}
//运算结果扔到管道
resultChan <- r
}
}(jobChan, resultChan)
}
}