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
}
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
type A interface{}
type B struct{}
func main() {
var b *B = nil
var a A = b
fmt.Println(a == nil) // Output: false
}
这里要简单说下接口实现,接口在Go语言中通过iface
和eface
两个结构体实现,其中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()
}