golang 学习笔记 - defer/数组/GC/channel

本文主要列出我曾犯错的地方,多来自于面试题。

defer

下列输出结果:

package main

func main() {
	i := 0
	defer func() {
		fmt.Println("func print:" + strconv.FormatInt(int64(i), 10))
	}()
	defer fmt.Println("first print:" + strconv.FormatInt(int64(i), 10))
	for ; i < 3; i++ {
		defer fmt.Println("second print:" + strconv.FormatInt(int64(i), 10))
	}
	defer fmt.Println("end print:" + strconv.FormatInt(int64(i), 10))
	i = 100
}

defer 后面语句的运行,如果是语句,那么其中变量的值是编译过程就会确定的,如果是闭包或者函数,则是在运行过程才能确定的。
输出结果:

end print:3
second print:2
second print:1
second print:0
first print:0
func print:100

数组,cap,len

cap([]interface{}) int 是数组实际的容量
len([]interface{}) int 是数组包含的元素个数

package main

func main() {
	i1 := make([]int, 3)
	fmt.Printf("i1 length is %d\n", len(i1)) // 3
	fmt.Printf("i1 cap is %d\n", cap(i1))    // 3
	fmt.Printf("i1 is %d\n", i1)			 // [0, 0, 0]

	i2 := make([]int, 3, 5)
	fmt.Printf("i2 length is %d\n", len(i2)) // 3
	fmt.Printf("i2 cap is %d\n", cap(i2))    // 5
	fmt.Printf("i2 is %d\n", i2)             // [0, 0, 0]

	i3 := i2[1:]
	fmt.Printf("i3 length is %d\n", len(i3)) // 2
	fmt.Printf("i3 cap is %d\n", cap(i3))    // 4
	fmt.Printf("i3 is %d\n", i3)             // [0, 0]
}

GC

go语言的gc机制,只要指针发生引用,即使是局部变量,也不会被GC。
下列代码输出:

package main

var s map[string]*Student

type Student struct {
	Name string
	Age  int
}

func Test() {
	i := make([]*Student, 3)
	i[0] = &Student{
		Name: "zhang",
		Age:  20,
	}
	i[1] = &Student{
		Name: "li",
		Age:  21,
	}
	i[2] = &Student{
		Name: "wang",
		Age:  19,
	}
	s = make(map[string]*Student, 3)
	for _, v := range i {
		s[v.Name] = v
	}
}

func main() {
	Test()
	for _, v := range s {
		fmt.Println(fmt.Sprintf("the value is : %v", v))
	}
}

输出结果:

the value is : &{zhang 20}
the value is : &{li 21}
the value is : &{wang 19}

channel

channel是go语言用于Goroutine通信的,用通信来共享内存,你可以声明各种类型的chan,可以是int,string,也可以是个函数指针。
chan的读和写通过符号 <- 来配合实现,例如:读 (i <- chan),写 (chan <- 1)。
chan的特性:读(写)chan之前,必须保证有Goroutine做写(读)的操作,如果读写语句都在同一Goroutine之内,就会发生死锁。

  1. 例如:死锁写法
package main

func main() {
	ch1 := make(chan int)
	fmt.Println("start")
	ch1 <- 1
	fmt.Println("write ch1")
	if <-ch1 == 1 {
		fmt.Println("ch1 is 1")
	}	
	fmt.Println("end")
}

输出结果:

start

chan写入以后,main就发生死锁了,因为没有别的Goroutine读。
解决办法一:将后续的读操作用闭包或者函数的形式,在chan写操作之前启动Goroutine

package main

func main() {
	ch1 := make(chan int)
	fmt.Println("start")
	go func() {
		fmt.Println("read ch1")
		i := <-ch1
		if i == 1 {
			fmt.Println("ch1 is 1")
		}
		fmt.Println("goruntine end")
	}()
	fmt.Println("write ch1")
	ch1 <- 1
	fmt.Println("end")
}

输出结果:

start
write ch1
read ch1
ch1 is 1
goruntine end
end

解决办法二:将chan提前声明容量,然后再写入,后续即使在同一个线程里读写,只要保证先写后读,就不会发生死锁

package main
func main() {
	ch1 := make(chan int, 1)
	fmt.Println("write ch1")
	ch1 <- 1
	fmt.Println("read ch1")
	if <-ch1 == 1 {
		fmt.Println("ch1 is 1")
	}
	fmt.Println("end")
}

输出结果:

write ch1
read ch1
ch1 is 1
end

解决方法三:使用select语句,select是专门为chan设计的,case条件必须是对chan的操作,如果所有case都没法满足,则会进入default。

package main

func main() {
	ch1 := make(chan int, 1)
	fmt.Println("write ch1")
	ch1 <- 1
	fmt.Println("select ch1")
	select {
	case i := <-ch1:
		fmt.Println(fmt.Sprintf("ch1 is %d", i))
	case ch1 <- 0:
		fmt.Println("write ch1 0")
	default:
		fmt.Println("all faild")
	}
	fmt.Println("end")
}

输出结果:

write ch1
select ch1
ch1 is 1
end

即使没有写入操作,也不会发生死锁,至于原因我在了解之后再细说吧。

package main

func main() {
	ch1 := make(chan int, 1)
	fmt.Println("select ch1")
	select {
	case i := <-ch1:
		fmt.Println(fmt.Sprintf("ch1 is %d", i))
	case ch1 <- 0:
		fmt.Println("write ch1 0")
	default:
		fmt.Println("all faild")
	}
	fmt.Println("end")
}

输出结果:

select ch1
write ch1 0
end
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值