单进程时代的两个问题:
思考:如果我把这个线程一分为二会怎么样?
cpu只能看见内核线程
如果M1对应处理器正在处理的G1阻塞住了,那么你猜猜P的本地队列里面的G2是等待直到阻塞结束呢?还是有什么好的办法可以让他不受阻塞影响,可以接着处理呢?
这里当然是后者了
GOMAXPROCS可以决定使用多少个CPU
相当于利用了时间片机制,每个goroutine最多被cpu宠幸10ms
当然会先去其他队列偷,如果其他队列没有,那么才会尝试去全局队列获取,因为去全局队列拿的话,需要加锁和解锁,比较浪费时间
package main
import (
"fmt"
"time"
)
//子goroutine
func newTask(){
i:=0
for{
i++
fmt.Printf("new GoRoutine: i=%d\n",i)
time.Sleep(1*time.Second)
}
}
func main() {
//创建一个go程去执行newTask()方法
go newTask();
i:=0
for{
i++
fmt.Printf("Main GoRoutine: i=%d\n",i)
time.Sleep(1*time.Second)
}
}
package main
import (
"fmt"
"runtime"
"time"
)
func main() {
//用go创建一个形参为空格,返回值为空的一个函数
go func(){
defer fmt.Println("A.defer")
func(){
defer fmt.Println("B.defer")
//退出当前goroutine
runtime.Goexit()//终止当前的goroutine
fmt.Println("B")
}()
fmt.Println("A")
}()//()表示匿名函数的调用
//这里go和主线程并行执行,如果学过java线程的小伙伴都懂,如果要在两个线程之间传递数据一般需要使用一个通道或者队列来存放共享数据
go func(a int,b int)bool{
fmt.Println("a= ",a," b= ",b)
return true
}(10,20)
//如果不等一下的话,主线程直接就结束了,goroutine还没来得及执行就死了
time.Sleep(time.Second*3)
}
package main
import (
"fmt"
"time"
)
func main() {
//定义一个channel
c:=make(chan int)
go func(){
defer fmt.Println("goroutine over!!!")
fmt.Println("goroutine is running!!!")
fmt.Println("sleeping 2 s")
time.Sleep(2*time.Second)
c <-666 //将666发送给c
time.Sleep(2*time.Second)
fmt.Println("repeat sleeping 2s")
}()
num:= <-c //从c中接收参数,并赋值给num
fmt.Println("num= ",num)
fmt.Println("main goroutine over!!!")
}
显然channel的作用就是用来同步的,那么同步机制是什么呢?
还有一种情况:
特点
有缓冲Channel使用演示:
package main
import (
"fmt"
"time"
)
func main() {
//定义一个带有缓冲的Channel
c:=make(chan int,3)
go func(){
defer fmt.Println("goroutine over!!!")
fmt.Println("goroutine is running!!!")
for i:=0;i<3 ;i++ {
c<-i
fmt.Println("发送的元素i= ",i," 通道长度len= ",len(c)," 通道容量cap= ",cap(c))
}
}()
time.Sleep(time.Second)
for i:=0;i<3 ;i++ {
num:=<-c
fmt.Println("num= ",num)
}
fmt.Println("main goroutine over!!!")
}
错误示范:
func main() {
//定义一个带有缓冲的Channel
c:=make(chan int,4)
go func(){
defer fmt.Println("goroutine over!!!")
fmt.Println("goroutine is running!!!")
for i:=0;i<5 ;i++ {
c<-i
fmt.Println("发送的元素i= ",i," 通道长度len= ",len(c)," 通道容量cap= ",cap(c))
}
}()
time.Sleep(time.Second)
//尝试从channel中读取第六个元素的时候会报错---因为此时没有goroutine会尝试往通道中写入数据
for i:=0;i<6;i++ {
num:=<-c
fmt.Println("num= ",num)
}
fmt.Println("main goroutine over!!!")
}
package main
import (
"fmt"
)
func main() {
//定义一个带有缓冲的Channel
c:=make(chan int)
go func(){
for i:=0;i<6;i++ {
c<-i
}
//close可以关闭一个channel
close(c)
}()
for{
//ok如果为true表示channel没有关闭,如果为false表示channel已经关闭
if data,ok :=<-c; ok{
fmt.Println(data)
}else {
break
}
}
fmt.Println("main goroutine over!!!")
}
package main
import (
"fmt"
)
func main() {
//定义一个带有缓冲的Channel
c:=make(chan int)
go func(){
for i:=0;i<6;i++ {
c<-i
}
//close可以关闭一个channel
close(c)
}()
/*
for{
//ok如果为true表示channel没有关闭,如果为false表示channel已经关闭
if data,ok :=<-c; ok{
fmt.Println(data)
}else {
break
}
}*/
//可以使用range来迭代不断操作channel
//如果channel有数据就循环读取一次,直到通道关闭,才会结束读取
for data:= range c{
fmt.Println(data)
}
fmt.Println("main goroutine over!!!")
}
单流程下⼀个go只能监控⼀个channel的状态,select可以完成监控多个channel的状态
伪代码:
以斐波那契数列为例吧:
package main
import "fmt"
func main() {
//无缓冲罐channel
c:=make(chan int)
quit:=make(chan int)
//sub go
go func(){
for i:=0;i<10;i++ {
//输出通道c里面的数据
//如果通道此时没有数据会阻塞等待
fmt.Println(<-c)
}
quit<-0
}()
//main go
fibonacii(c,quit)
}
func fibonacii(c , quit chan int) {
x,y:=1,1
for{
select{
//如果向通道c写入数据x成功,那么会进入下面这个分支语句
case c<-x:
x=y
y=x+y
//如果quit通道成功读取到了数据,则进入该分支语句
case <-quit:
fmt.Println("quit")
return
}
}
}
select具备多路channel的监控状态功能