Go基础知识梳理(四)

Go基础知识梳理(四)

Go基础知识梳理(四)

GO的哲学是“不要通过共享内存来通信,而是通过通信来共享内存”,通道是GO通过通信来共享内存的载体。

rumtime包常用方法

runtime.NumGoroutine() //返回当前程序的协程数量
runtime.GOMAXPROCS(0) //获取当前的GOMAXPROCS数量
runtime.GOMAXPROCS(2) //设置程序的GOMAXPROCS数量为2

goroutine

特性

  • 执行是非阻塞的,不会等待
  • go 后面的返回值会被忽略
  • 调度器不能保证goroutine的执行顺序
fun main() {
    //另起一个协程去打印
    go func() {
        fmt.Println("goruntine") 
    }()
}

chan

//创建chan
c := make(chan dataType)
//无缓冲, len 和 cap 都是0
fmt.Println(len(c)) //0
fmt.Println(cap(c)) //0


//有缓冲
c = make(chan dataType, 10)
//len 代表没有被读取的元素数, cap 代表整个通道的容量
fmt.Println(len(c)) //0
fmt.Println(cap(c)) //10

WaitingGroup组件

Add(10) 给内置计数器加10
Done() 相当于Add(-1)
Wait() 内置计数器不为0则一直等待

// 写法
func main() {
	var wg sync.WaitGroup
	wg.Add(10)
	for i := 0; i < 10; i++ {
		go func(i int) {
			defer wg.Done()
			fmt.Println(i)
			time.Sleep(time.Millisecond * 3000)
		}(i)
	}
	wg.Wait()
	fmt.Println("done")
}

select

  1. 先打乱所有的case语句
  2. 遍历case语句,查看是否已经读写了
  3. 选择可读写的case去执行
  4. 没有可读写的case,则查看default语句是否定义,再去执行
  5. 没有defaul语句,则会等待可执行的case

看一段融合了并发,缓冲,退出通知等多重特性的代码

func GenerateA(done chan struct{}) chan int {
	ch := make(chan int, 5)
	// fmt.Println("通道数+1")
	go func() {
		fmt.Println("线程数+A")
	Label:
		for {
			select {
			case res := <-done:
				fmt.Println("done", res)
				break Label
			case ch <- rand.Int():
			}

		}
		close(ch)
	}()
	return ch
}
func GenerateB(done chan struct{}) chan int {
	ch := make(chan int, 5)
	go func() {
		fmt.Println("线程数+B")
	Label:
		for {
			select {
			case res := <-done:
				fmt.Println("done", res)
				break Label
			case ch <- rand.Int():
			}
		}
		close(ch)
	}()
	return ch
}

func GenerateInt(done chan struct{}) chan int {
	//无缓冲通道
	ch := make(chan int)
	send := make(chan struct{})
	go func() {
	Label:
		for {

			select {
			case <-done:
				send <- struct{}{}
				send <- struct{}{}
				break Label
			case ch <- <-GenerateA(send):
				fmt.Println("chose A")
			case ch <- <-GenerateB(send):
				fmt.Println("chose B")
			}
		}
		close(ch)
	}()
	return ch
}

func main() {
	done := make(chan struct{})
	ch := GenerateInt(done)
	fmt.Println(runtime.NumGoroutine())
	for i := 0; i < 10; i++ {
		fmt.Println(<-ch)
	}
	fmt.Println(runtime.NumGoroutine())
	done <- struct{}{}
	fmt.Println("stop gernate", struct{}{})
	time.Sleep(1 * time.Second) //等待1s,让停止的goruntime打印,如果不加这句话,可能会导致主线程比新起的协程早退出,从而无法打印出done {}
}

打印结果不展示,其中发现的问题:
1.打印出来的数字长度不固定
2.每次Select查看case是否堵塞的时候,都会执行一次该方法

Context

func main() {
	ctxa, cancel := context.WithCancel(context.Background())
	go work(ctxa, "work1")
	tm := time.Now().Add(3 * time.Second)
	ctxb, _ := context.WithDeadline(ctxa, tm)
	go work(ctxb, "work2")
	oc := OtherContext{ctxb}
	ctxc := context.WithValue(oc, "key", "andes, pass from main")
	go workWithValue(ctxc, "work3")
	time.Sleep(10 * time.Second)
	cancel()
	time.Sleep(5 * time.Second)
	fmt.Println("main stop")

}

type OtherContext struct {
	context.Context
}

func work(ctx context.Context, name string) {
	for {
		select {
		case <-ctx.Done():
			fmt.Printf("%s get msg to cancel
", name)
			return
		default:
			fmt.Printf("%s is running 
", name)
			time.Sleep(1 * time.Second)
		}
	}
}

func workWithValue(ctx context.Context, name string) {
	for {
		select {
		case <-ctx.Done():
			fmt.Printf("%s get msg to cancel
", name)
			return
		default:
			value := ctx.Value("key").(string)
			fmt.Printf("%s is running value=%s 
", name, value)
			time.Sleep(1 * time.Second)
		}
	}
}

这段代码中,都是一条链路下来的
根节点 context.Background() -> ctxa -> ctxb -> oc
其中ctxa加了个时间控制,所以到达一定的时间就会自动关闭
oc附带了个键对值

hmoban主题是根据ripro二开的主题,极致后台体验,无插件,集成会员系统
自学咖网 » Go基础知识梳理(四)