Go | 常见坑

Go | 常见坑

1. rand

import (
	"math/rand"
	"time"
)

var generator = rand.New(rand.NewSource(time.Now().Unix()))

func main() {
	go func() {
		for {
			generator.Int()
		}
	}()

	go func() {
		for {
			generator.Int()
		}
	}()

	time.Sleep(time.Second * 100)
}

Output

panic: runtime error: index out of range [-1]
...

我们使用rand.New创建随机数生成器不是线程安全,多协和同时访问会出现异常。直接使用rand包下的rand.Int()确不会有异常,是因为系统对rand.Int()做了包装,下面看下rand.Int()具体实现。

// 调用全局随机数生成器
func Int() int { return globalRand.Int() }

下面在看下globalRand定义,系统默认对Source外层用锁包裹,以此来达到并发访问安全。

var globalRand = New(&lockedSource{src: NewSource(1).(*rngSource)})

使用全局的rand注意在项目启动最开始使用随机数种子初始化,全局随机数生成器,代码如下。

func main() {
	// 初始化全局随机数种子
	rand.Seed(time.Now().Unix())
	fmt.Println(rand.Int()) // Output: 5496087833341174706
	fmt.Println(rand.Int()) // Output: 7529864966967492356
}

2. for

func myPrint(num *int) {
	fmt.Printf("num: %d point: %p\n", *num, num)
}

func main() {
	arr := []int{1, 2, 3, 4, 5}
	for _, v := range arr {
		// 整个for循环周期中变量v是共用一个
		// 每次循环相当于给v重新赋值
		go myPrint(&v)
	}

	time.Sleep(time.Second)
}

Output

num: 5 point: 0xc000018090
num: 4 point: 0xc000018090
num: 5 point: 0xc000018090
num: 5 point: 0xc000018090
num: 5 point: 0xc000018090

3. interface == nil

type A interface{}

type B struct{}

func main() {
	var b *B = nil
	var a A = b
	fmt.Println(a == nil) // Output: false
}

这里要简单说下接口实现,接口在Go语言中通过ifaceeface两个结构体实现,其中iface表示有接口方法的,eface表示没有接口方法的接口,接口内部实现如下。

type iface struct {
	tab  *itab
	data unsafe.Pointer
}

type eface struct {
	_type *_type
	data  unsafe.Pointer
}

而我们一但给一个接口赋值一个实例,哪怕实例是nil,接口中tab_type字段就有了当前实例的类型信息,接口就不能直接在与nil判断,可能使用如下方法判断。

type A interface{}

type B struct{}

func main() {
	var b *B = nil
	var a A = b
	fmt.Println(a == nil) // Output: false
	fmt.Println(IsNil(a)) // Output: true
}

func IsNil(a A) bool {
	if a == nil {
		return true
	}

	return reflect.ValueOf(a).IsNil()
}

你可能感兴趣的:(Go,go,golang,坑,rand,接口不等于nil)