1.go-内联优化机制,如下代码描述
package main
import "fmt"
/*
go 内联优化,根据AST语法树的字符个数,(80) >80 进行优化内联
*/
func add(a, b int) int {
return a + b
}
//go:noinline ,屏蔽掉系统的内联优化,通过go build -gcflags="-m -m" goInline.go查看
// ./goInline.go:5:6: can inline add with cost 4 as: func(int, int) int { return a + b }
// ./goInline.go:10:6: cannot inline sub: marked go:noinline
// ./goInline.go:13:6: cannot inline main: function too complex: cost 229 exceeds budget 80
// ./goInline.go:16:17: inlining call to add func(int, int) int { return a + b }
func sub(a, b int) int {
return a - b
}
func main() {
a := 1
b := 2
fmt.Println(add(a, b))
fmt.Println(sub(a, b))
}
2.for循环中的坑
例如:
package main
import "fmt"
func main() {
in := []int{1, 2, 3}
var out []*int
// for _, v := range in {
// out = append(out, &v)
// }
// for _, n := range out {
// fmt.Println(n)
// }
// 如下结果都是一样,产生如此的原因,v是单个变量,(内存地址是不变的,每次迭代都采用的新值,并且他们都指向了相同的值)
// 0xc0000160c0
// 0xc0000160c0
// 0xc0000160c0
// 解决:使用一个中间变量犯法
for _, v := range in {
v := v
out = append(out, &v)
}
for _, n := range out {
fmt.Println(n)
}
}
// 除此之外,避免在循环内使用Wait,defer等方法。根据实际情况处理。
3.channel阻塞
1 func doReq(timeout time.Duration) obj {
2 // ch :=make(chan obj)
3 ch := make(chan obj, 1)
4 go func() {
5 obj := do()
6 ch <- result
7 } ()
8 select {
9 case result = <- ch:
10 return result
11 case <- time.After(timeout):
12 return nil
13 }
14 }
如上代码,子 goroutine 执行 do 函数并通过第 6 行的通道 ch 将结果发送回父 goroutine。
子 goroutine 将在第 6 行阻塞,直到父 goroutine 在第 9 行从 ch 接收到结果为止。同时,
父 goroutine 将在 select 阻塞,直到子 goroutine 将结果发送给 ch(第 9 行)或超时
(第 11 行)。如果超时先发生,则父 goroutine 将从 doReq 第 12 行返回,这会导致没有 goroutine 从 ch 读取数据,子 goroutine 就会一直堵塞在第 6 行。解决办法是将 ch
从无缓冲的通道改为有缓冲的通道,因此子goroutine 即使在父 goroutine 退出后也始终
可以发送结果。
***无缓冲通道
定义一个通道时,容量为0或者不设置容量,即为无缓冲通道,此通道特点,必须发送者&接受者都就绪了才能通信
package main
import (
"fmt"
"time"
)
func main() {
// 创建一个无缓存的channel
ch := make(chan int, 0)
// len(ch)缓冲区剩余数据个数, cap(ch)缓冲区大小,两者这里永远都是0
fmt.Printf("len(ch) = %d, cap(ch)= %d\n", len(ch), cap(ch))
// 新建协程
go func() {
for i := 0; i < 3; i++ { // 写三次
fmt.Printf("子协程:i = %d\n", i)
ch <- i // 往chan写内容
fmt.Printf("len(ch) = %d, cap(ch)= %d\n", len(ch), cap(ch))
}
}()
// 延时2秒
time.Sleep(2 * time.Second)
for i := 0; i < 3; i++ { // 必须读三次
num := <-ch // 读管道中内容,没有内容前,阻塞
fmt.Println("num = ", num)
}
}
缓冲通道:
package main
import (
"fmt"
"time"
)
func main() {
//创建一个有缓存的channel
ch := make(chan int, 3) //容量是3
//len(ch)缓冲区剩余数据个数, cap(ch)缓冲区大小
fmt.Printf("len(ch) = %d, cap(ch)= %d\n", len(ch), cap(ch))
//新建协程
go func() {
for i := 0; i < 10; i++ { //这里数据量大于管道容量,会出阻塞
ch <- i //往chan写内容,如果主协程没读的话,写满3个就会阻塞在此
fmt.Printf("子协程[%d]: len(ch) = %d, cap(ch)= %d\n", i, len(ch), cap(ch))
}
}()
//延时
time.Sleep(2 * time.Second)
for i := 0; i < 10; i++ { //这里数据量大于管道容量,会出阻塞
num := <-ch //读管道中内容,没有内容前,阻塞
fmt.Println("num = ", num)
}
}
总结:
无缓冲: 不仅仅是向 c1 通道放 1,而是一直要等有别的携程 <-c1 接手了这个参数,
那么c1<-1才会继续下去,要不然就一直阻塞着。
有缓冲: c2<-1 则不会阻塞,因为缓冲大小是1(其实是缓冲大小为0),只有当放第二
个值的时候,第一个还没被人拿走,这时候才会阻塞。