多线程和多进程存在的问题是耗费内存,线程切换
其他语言的协程: php-swoole java-netty
协程优势:切换快 内存占用小(2k)
- golang的线程模型是GMP模型:
- 在语言层面是没有开放线程的但是在运行时候M个线程可以创建N个goroutine,一般N远大于M本质属于多线程模型,但是协程的调用由goruntine决定,强调开发者应该使用channel进行协程之间的同步
- GMP模型,
- goroutine在M个线程上运行,每次执行任务都会去查当前P(处理器)上的可执行队列,队列里面就是可以执行的goroutine,一旦当前P上没有可供执行的它就会去窃取另一个P的可执行队列中的goroutine。而goroutine的创建理论上来说只受内存限制,一般而言最大也就2KB,对于一个线程2MB空间大小理论上来说可以轻易上1000个,当然这只是理想情况下,所以OS线程数不会随着goroutine创建个数增加而增加。线程调度是对于go来说会比较耗费性能,频繁切换调度只会在goroutine之间存在,线程只会保持与cpu数相同的活跃线程数。
- 协程切换
- 如果协程阻塞有一些需要占用M处理资源(读取文件等操作)的时候会P(处理器)会将他挂起一个M(线程)阻塞,P就会去创建或者切换另一个M继续执行队列中后续的goroutine
package main
import (
"fmt"
"time"
)
/**
并发编程
*/
//主协程
func main() {
//启动子协程,通过go关键字启动协程
go asyncPrint()
//匿名函数方式启动协程
go func() {
i := 1
for{
fmt.Println("async print2 ",i)
i++
if i>100 {
return
}
}
}()//最后一个括号是执行的意思func(){}只是定义
//1.
for i:=0;i<100;i++{
//闭包导致里面主函数里面的匿名函数可以使用主函数的变量
go func() {
j := 1
//for{
//1.这里打印,i的值会因为外面的循环i++导致靠前的值不会打印因为i没有进行复制,和外面还是用的同一个变量,变量指向的堆中的值还是一样的,导致外面改变了堆中的值这里也会收到影响,
//2.并且打印乱是因为多个协程执行顺序可能不一样
//3.将其复制
tmp := i
fmt.Println(i,"async print3 ",j,tmp)
j++
//}
}()
//4.使用值传递方式
go func(i int) {
j := 1
tmp := i
fmt.Println(i,"async print4 ",j,tmp)
j++
}(i)
}
fmt.Println("main ok")
//这里存在主死随从,所以主协程需要等子协程执行完
time.Sleep(2*time.Second)
}
func asyncPrint(){
i := 1
for{
fmt.Println("async print1 ",i)
i++
if i>100 {
return
}
}
}