传统进程和线程概念介绍:
并发和并行概念介绍:
golang协程(goroutine)
golang协程的特点
- 有独立的栈空间(栈可理解为数值类型,由堆中copy或引用到个体栈空间)
- 共享程序堆空间(堆可理解为引用数据类型)
- 调度由用户控制(线程启动和停止都可由用户控制,java则不行 )
- 协程是轻量级的线程(理论上轻松可启上万条线程)
package main
import (
"fmt"
"strconv"
"time"
)
func goroutine(){
for i:=1;i<=10;i++{
fmt.Println("hello world" + strconv.Itoa(i))
time.Sleep(time.Second)
}
}
func main(){
// 开启一个协程
go goroutine()
for i:=1;i<=10;i++{
fmt.Println("hello goroutine" + strconv.Itoa(i))
time.Sleep(time.Second)
}
}
PowerShell输出:
PS E:\...\src\gojson\jsondemo-10-24\goroutine> go run .\main.go
hello world1
hello goroutine1
hello goroutine2
hello world2
hello world3
hello goroutine3
hello goroutine4
hello world4
hello world5
hello goroutine5
hello goroutine6
hello world6
hello world7
hello goroutine7
hello goroutine8
hello world8
hello world9
hello goroutine9
hello goroutine10
hello world10
go协程执行过程:
go协程总结:
- go协程一般是以函数为单位的
- 协程依托于主线程执行,主线程执行完毕或停止退出则协程也直接退出,即使没有执行完毕,若协程先于主线程执行完毕则主线程继续执行完毕
- go协程中,主线程可以理解为类似进程,协程类似于线程
goroutine调度模型MPG
- M:主线程,直接运行在操作系统上,为物理线程
- P:程序运行需要的资源或者说依赖
- G:协程是一个逻辑程序依赖于M主线程 运行,有自己的栈独立运行
- M可以有多个,多个M运行在一个cpu上叫做并发,多个M运行在多个cpu叫做并行
- 如果一个协程发生了阻塞程序会另外开启一个线程把排队的协程引用到这个新开启的M中去,等到M0中的G不再堵塞则再进行M0的调度执行,保证程序得以继续并发执行
golang中设置允许cpu数目
package main
import (
"fmt"
"runtime"
)
func main(){
// runtime.NumCPU() 查看本机有几颗逻辑cpu
cpunum := runtime.NumCPU()
fmt.Println(cpunum)
// runtime.GOMAXPROCS() 可以设置可同时执行最大cpu数目
runtime.GOMAXPROCS(cpunum-1)
// runtime.Version() 返回go版本字符串,1.8以后默认多核允许不需要设置
govarson := runtime.Version()
fmt.Println(govarson)
}
PS E:\...\src\gojson\jsondemo-10-24\cupdemo> go run .\main.go
2
go1.9.2
channel管道
多个协程资源竞争问题:
- 下边这个程序我们通过一个 test() 函数,计算传入参数的阶乘并写入map中,主函数里开启200个协程来进行相应阶乘运算此时由于多个协程同时往一个map中写入数据就会出错,资源占用冲突
package main
import (
"fmt"
"time"
)
var mymap = make(map[int]int,10)
func test(n int){
res := 1
for i:=1;i<=n;i++{
res *= i
}
mymap[n] = res
}
func main(){
// 通过循环开启200个协程
for i:=0;i<200;i++{
go test(i)
}
// 休眠十秒钟
time.Sleep(time.Second * 10)
for k,v := range mymap{
fmt.Println(k,v)
}
}
- Mutex是一个结构体,有各种绑定这个结构体的方法,如 lock() ,将传入的参数锁住,Unlock() 方法可以解锁,锁和线程无关可以由任何线程加锁和解锁
package main
import (
"fmt"
"time"
// "sync"
)
var (
// int类型默认为int64位大小为 2的63次方减一等于9223372036854776000
// 也就是说只能存放这么大的数字超出范围置为0
mymap = make(map[int]int)
// 实例化sync.Nutex这个struct,以便使用绑定这个struct的方法
// 这里也是一个小技巧,实例化结构体
lock sync.Mutex
)
func test(n int){
res := 1
for i:=1;i<=n;i++{
res *= i
}
// 加锁
lock.