1.进程、线程小知识
进程是程序在os中的一次执行过程。是资源分配和调度的最小单位
线程是程序执行的最小单位,
同一个进程中的多个线程可以并发执行
一个程序至少一个进程,一个进程至少一个线程
2.go主线程(有时直接叫线程/可以理解为传统意义上的进程)
在一个go线程上,可以起多个协程(可以理解为轻量的线程)
3.go协程的特点
·独立的栈空间
·共享程序的堆空间
·用户控制调度
·协程是轻量级的线程
4.协程sample
//主线程(进程)中 开启一个roroutine 每隔1s输出
//主线程也每隔一秒输出 同时执行
func test() {
for i := 1; i <= 10; i++ {
fmt.Println("test() hello, world " + strconv.Itoa(i))
time.Sleep(time.Second)
}
}
func main(){
go test() //开启了一个协程
for i := 1; i <= 10; i++ {
fmt.Println("main() hello, golang " + strconv.Itoa(i))
time.Sleep(time.Second)
}
}
可以理解为主线程运行到go语句 开启一个协程分支独立执行
如果主线程推出了,协程还没有执行完,也会退出
5.主线程的物理的,直接作用在cpu上的。是重量级的,非常耗费cpu资源
协程是主线程开启的,是逻辑态,对资源消耗相对小
golang可以轻松开启上万个协程,并发优势巨大
6.MPG模式
M: os的主线程(物理线程)
P:协程执行所需要的上下文
G:协程
7.golang
查询cpu数量,设置使用多少个cpu
cpuNum := runtime.NumCPU()
fmt.Println("cpuNum =", cpuNum)
//可以自己设置使用多少个CPU
runtime.GOMAXPROCS(cpuNum - 1)
fmt.Println("ok")
1.8以后默认程序运行在多个核上 不用设置
8.并发势必会出现资源冲突的问题
如何知道是否出现资源冲突
go build -race test.go
test.go
9.互斥锁
只适用于低水平的并发程序
//计算1-200的各个数的阶乘 并放到map
//用goroutine完成 启动多个协程 将结果放入map中
//map声明为全局的
//避免出现concurrent map writes
var (
mp = make(map[int]int, 10)
//全局的互斥锁
lock sync.Mutex
)
func test(n int) {
res := 1
for i := 1; i <= n; i++{
res *= i
}
lock.Lock()
mp[n] = res
lock.Unlock()
}
func main(){
for i := 1; i <= 20; i++ {
go test(i)
}
//要休眠 不然主线程直接跑完了
time.Sleep(time.Second * 5)
//读也要加锁 否则还会冲突
lock.Lock()
for i, v := range mp{
fmt.Printf("mp[%v] = %v\n", i, v)
}
lock.Unlock()
}
用全局变量的互斥锁和主线程睡眠 解决了问题
但是很蠢
管道方法见下一篇