Go: 谨慎使用 math/rand 包中的默认随机数函数

栏目: Go · 发布时间: 6年前

内容简介:一句话总结:math/rand 包中默认的随机数相关函数共享了一个全局锁, 即:所有使用默认随机函数的代码都会去竞争一个全局锁,有时这可能不是你想要的结果。

一句话总结:math/rand 包中默认的随机数相关函数共享了一个全局锁, 即:所有使用默认随机函数的代码都会去竞争一个全局锁,有时这可能不是你想要的结果。

比如 rand.Int63 这个函数的 源代码 如下:

func Int63n(n int64) int64 { return globalRand.Int63n(n) }

可以看到它其实是调用了一个全局的 Rand 实例 globalRand ,我们来看一下 globalRand定义 :

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

通过 New 的源码以及 globalRand.Int63n 的源码可以看到关键点是 lockedSource.Int63 方法的定义:

 type lockedSource struct {
     lk  sync.Mutex
     src Source64
 }

 func (r *lockedSource) Int63() (n int64) {
     r.lk.Lock()
     n = r.src.Int63()
     r.lk.Unlock()
     return
 }

通过同样的方法查看其他默认的随机函数可以发现,所有的默认随机函数都共享了一个全局锁,调用这些默认随机函数的时候都会先进行一次获取锁的操作。

大部分情况下不需要管这个全局锁的问题,因为大部分情况下都不会介意这点性能消耗。 如果确实特别在意这点性能消耗的话,可以通过定义一个你的包共享的或者结构体实例共享的 Rand 实例来优化锁的性能消耗(最小化锁的粒度,不跟其他包/代码竞争这个锁)。

例子:

type Xyz struct {
    // Rand 实例不是并发安全的,需要自行解决并发安全问题
    rndMu sync.Mutex
    rnd *rand.Rand
}

func (x *Xyz) random() int32 {
    x.rndMu.Lock()
    n := x.rnd.Int31()
    x.rndMu.Unlock()

    return n
}

func main() {
    x := &Xyz{
        rnd: rand.New(rand.NewSource(time.Now().UnixNano())),
    }
    fmt.Println(x.random())
}

或者可以考虑使用性能更好的第三方 rand 包: valyala/fastrand


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

虚拟化与云计算

虚拟化与云计算

《虚拟化与云计算》小组 / 电子工业出版社 / 2009-10 / 45.00元

本书系统阐述了当今信息产业界最受关注的两项新技术——虚拟化与云计算。云计算的目标是将各种IT资源以服务的方式通过互联网交付给用户。计算资源、存储资源、软件开发、系统测试、系统维护和各种丰富的应用服务,都将像水和电一样方便地被使用,并可按量计费。虚拟化实现了IT资源的逻辑抽象和统一表示,在大规模数据中心管理和解决方案交付方面发挥着巨大的作用,是支撑云计算伟大构想的最重要的技术基石。本书以在数据中心采......一起来看看 《虚拟化与云计算》 这本书的介绍吧!

MD5 加密
MD5 加密

MD5 加密工具

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试