GoLang中的十个初学者常见坑

以下GoLang常见坑都是符合语法且通过编译,但可能运行结果错误或有资源泄漏的风险。

代码示例

1. 注意参数展开的问题。
2. 数组是值传递,无法通过修改参数返回结果。
3. map是一种hash表实现,每次遍历的顺序可能不一样。
4. recover必须在defer中执行。
5. 避免独占CPU Goroutine是协作式抢占调度,Goroutine本身不会主动放弃CPU
6. 闭包错误引用同一个变量
7. defer在函数退出时才能执行,在for执行defer会导致资源延迟释放。
8. 切片会导致整个底层数组被锁定,无法释放内存。
9. 内存地址会变化。
10. Goroutine泄漏。

1. 注意参数展开的问题。

func m1() {
	var a = []interface{}{1, 2, 3}

	fmt.Println(a)
	fmt.Println(a...)
	/**
	1. [1 2 3]
	2. 1 2 3
	*/
}

2. 数组是值传递,无法通过修改参数返回结果。

func m2() {
	x := [3]int{1, 2, 3}
	func(arr [3]int) {
		arr[0] = 7
		fmt.Println(arr)
	}(x)

	fmt.Println(x)

	/**
	[1, 2, 3]
	*/
}

3. map是一种hash表实现,每次遍历的顺序可能不一样。

func m3() {
	m := map[string]string{
		"1": "1",
		"2": "2",
		"3": "3",
	}

	for k, v := range m {
		println(k, v)
	}
}

4. recover必须在defer中执行。

func m4() {
	//defer recover()
	//panic(1) error

	//defer func() {
	//	func() {
	//		recover()
	//	}()
	//}()
	//panic(1) error

	defer func() {
		recover()
	}()
	panic(1)
}

5. 避免独占CPU Goroutine是协作式抢占调度,Goroutine本身不会主动放弃CPU

5. 避免独占CPU Goroutine是协作式抢占调度,Goroutine本身不会主动放弃CPU。

func m5() {
	// 1
	runtime.GOMAXPROCS(1)
	go func() {
		for i := 0; i < 10; i++ {
			fmt.Println(i)
		}
	}()
	for {
		runtime.Gosched()
	}

	// 2
	go func() {
		for i := 0; i < 10; i++ {
			fmt.Println(i)
		}
		os.Exit(0)
	}()
	select {}
}

6. 闭包错误引用同一个变量

6. 闭包错误引用同一个变量。

func m6() {
	for i := 0; i < 3; i++ {
		defer func() {
			println(i)
		}()
	}
	// error
	// 3, 3, 3

	for i := 0; i < 3; i++ {
		defer func(i int) {
			println(i)
		}(i)
	}
	// correct
	// 0, 1, 2
}

7. defer在函数退出时才能执行,在for执行defer会导致资源延迟释放。

func m7() {
	for i := 0; i < 3; i++ {
		f, err := os.Open("path")
		if err != nil {
			log.Fatal(err)
		}
		defer f.Close()
	} //error

	for i := 0; i < 3; i++ {
		func() {
			f, err := os.Open("path")
			if err != nil {
				log.Fatal(err)
			}
			defer f.Close()
		}()
	} // correct
}

8. 切片会导致整个底层数组被锁定,无法释放内存。

func m8() {
	headerMap := make(map[string][]byte)
	for i := 0; i < 3; i++ {
		name := "/path"
		data, err := os.ReadFile(name)
		if err != nil {
			log.Fatal(err)
		}
		// headerMap[name] = data[:1] error
		headerMap[name] = append([]byte{}, data[:1]...) // correct
	}
}

9. 内存地址会变化。

func m9() {
	var x int = 32
	var p uintptr = uintptr(unsafe.Pointer(&x))
	runtime.GC()
	var px *int = (*int)(unsafe.Pointer(p))
	println(*px)
	// 当内存发送变化的时候,相关的指针会同步更新,但是非指针类型的uintptr不会做同步更新。
	// 同理CGO中也不能保存Go对象地址。
}

10. Goroutine泄漏。

func m10() {
	//ch := func() <-chan int {
	//	ch := make(chan int)
	//	go func() { // 无法回收
	//		for i := 0; ; i++ {
	//			ch <- i
	//		}
	//	}()
	//	return ch
	//}()
	//
	//for v := range ch {
	//	fmt.Println(v)
	//	if v == 5 {
	//		break
	//	}
	//}

	ctx, cancel := context.WithCancel(context.Background())
	ch := func(ctx context.Context) <-chan int {
		ch := make(chan int)
		go func() {
			for i := 0; ; i++ {
				select {
				case <-ctx.Done():
					return
				case ch <- i:
				}
			}
		}()
		return ch
	}(ctx)

	for v := range ch {
		fmt.Println(v)
		if v == 5 {
			break
		}
	}
	cancel()
}

代码仓库

本文代码仓库链接

相关图书链接
https://www.bookstack.cn/read/advanced-go-programming-book/README.md
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值